Complete API guide with examples and integration patterns
Version: 1.1.0
Last Updated: January 27, 2026
Status: Production Ready
API Base URL: https://perti.vatcscc.org/api/swim/v1
WebSocket URL: wss://perti.vatcscc.org/api/swim/v1/ws
> FIXM Migration Complete (2026-01-27)
>
> The VATSWIM API now uses FIXM-aligned field naming exclusively. Legacy OOOI column names have been replaced:
>
> | Legacy Name (Removed) | FIXM-Aligned Name (Current) |
> | --------------------- | --------------------------- |
> | out_utc | actual_off_block_time |
> | off_utc | actual_time_of_departure |
> | on_utc | actual_landing_time |
> | in_utc | actual_in_block_time |
> | eta_utc | estimated_time_of_arrival |
> | etd_utc | estimated_off_block_time |
>
> After FIXM cutover:
>
> - SWIM database uses FIXM column names only
> - API responses use FIXM field names exclusively
> - ADL database retains legacy column names (source of truth)
> - Ingest endpoints accept both legacy and FIXM input names for compatibility
>
> See VATSWIM_FIXM_Field_Mapping.md for complete mapping.
1. Overview
5. Data Models
6. Python SDK
VATSWIM (System Wide Information Management) is a comprehensive API that provides access to real-time and historical flight data from the VATSIM virtual air traffic control network. The API follows FAA SWIM and FIXM (Flight Information Exchange Model) standards to provide standardized flight data to external consumers.
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ VATSIM Network │─────▶│ ADL Daemon │─────▶│ SWIM_API DB │
│ (live data) │ │ (15s refresh) │ │ (Azure SQL) │
└─────────────────┘ └────────┬────────┘ └────────┬────────┘
│ │
│ events │ queries
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ WebSocket │ │ REST API │
│ Server │ │ Endpoints │
└────────┬────────┘ └────────┬────────┘
│ │
┌─────────────┴────────────────────────┴─────────────┐
│ SWIM Clients │
│ (vNAS, CRC, SimAware, Virtual Airlines, etc.) │
└─────────────────────────────────────────────────────┘
| Component | Monthly Cost |
| SWIM_API Database (Azure SQL Basic) | $5 |
| WebSocket Server (self-hosted) | $0 |
| Total | $5/month |
All API requests require authentication via Bearer token or query parameter.
Header Authentication (Recommended):
Authorization: Bearer swim_dev_your_key_here
Query Parameter (WebSocket):
wss://perti.vatcscc.org/api/swim/v1/ws?api_key=swim_dev_your_key_here
| Tier | Prefix | Rate Limit | Max WS Connections | Write Access |
| system | swim_sys_ | 30,000/min | 10,000 | Yes |
| partner | swim_par_ | 3,000/min | 500 | Limited |
| developer | swim_dev_ | 300/min | 50 | No |
| public | swim_pub_ | 100/min | 5 | No |
API keys are stored in the dbo.swim_api_keys table in VATSIM_ADL database.
INSERT INTO dbo.swim_api_keys (api_key, tier, owner_name, owner_email, description)
VALUES (
'swim_dev_' + LOWER(CONVERT(VARCHAR(36), NEWID())),
'developer',
'Developer Name',
'email@example.com',
'API key for development testing'
);
The system validates keys against the database with a 5-minute cache TTL:
is_active = 1expires_at is null or in the futurelast_used_at on successful authenticationEndpoint: GET /api/swim/v1
Returns API information and available endpoints.
Response:
{
"success": true,
"data": {
"name": "VATSWIM API",
"version": "1.0.0",
"description": "System Wide Information Management for VATSIM",
"documentation": "https://perti.vatcscc.org/docs/swim/",
"endpoints": {
"flights": {
"GET /api/swim/v1/flights": "List flights with filters",
"GET /api/swim/v1/flight": "Get single flight by GUFI or flight_key"
},
"positions": {
"GET /api/swim/v1/positions": "Bulk flight positions (GeoJSON)"
},
"tmi": {
"GET /api/swim/v1/tmi/programs": "Active TMI programs (GS/GDP)",
"GET /api/swim/v1/tmi/controlled": "Flights under TMI control"
}
}
}
}
Endpoint: GET /api/swim/v1/flights
Returns paginated list of flights with optional filtering.
Query Parameters:
| Parameter | Type | Description |
status | string | active (default), completed, or all |
dept_icao | string | Comma-separated departure airports (e.g., KJFK,KLGA) |
dest_icao | string | Comma-separated destination airports |
artcc | string | Comma-separated ARTCCs (e.g., ZNY,ZBW) |
callsign | string | Callsign pattern with wildcards (e.g., UAL) |
tmi_controlled | boolean | true to filter TMI-controlled flights |
phase | string | Flight phase filter (e.g., ENROUTE,DESCENDING) |
format | string | Response format (see below) |
page | int | Page number (default: 1) |
per_page | int | Results per page (default: 100, max: 1000) |
| Format | Content-Type | Description |
json | application/json | Standard JSON with snake_case fields (default) |
fixm | application/json | JSON with FIXM 4.3.0 camelCase field names |
xml | application/xml | XML format for enterprise/SOAP integrations |
geojson | application/geo+json | GeoJSON FeatureCollection for mapping (Leaflet, Mapbox) |
csv | text/csv | CSV for spreadsheet/analytics export |
kml | application/vnd.google-earth.kml+xml | KML for Google Earth visualization |
ndjson | application/x-ndjson | Newline-delimited JSON for streaming/bulk processing |
curl -H "Authorization: Bearer swim_dev_test" \
"https://perti.vatcscc.org/api/swim/v1/flights?dest_icao=KJFK&status=active"
Response (Legacy Format):
{
"success": true,
"data": [
{
"gufi": "VAT-20260116-UAL123-KLAX-KJFK",
"flight_uid": 12345,
"flight_key": "UAL123_KLAX_KJFK_20260116",
"identity": {
"callsign": "UAL123",
"cid": 1234567,
"aircraft_type": "B738",
"aircraft_icao": "B738",
"weight_class": "L",
"wake_category": "M",
"airline_icao": "UAL",
"airline_name": "United Airlines"
},
"flight_plan": {
"departure": "KLAX",
"destination": "KJFK",
"alternate": "KEWR",
"cruise_altitude": 35000,
"cruise_speed": 450,
"route": "DOTSS5 KAYOH J146 ABQ J80 HVE J100 STL J24 JHW LENDY5",
"flight_rules": "I",
"departure_artcc": "ZLA",
"destination_artcc": "ZNY",
"arrival_fix": "LENDY",
"arrival_procedure": "LENDY5"
},
"position": {
"latitude": 40.1234,
"longitude": -74.5678,
"altitude_ft": 35000,
"heading": 85,
"ground_speed_kts": 480,
"vertical_rate_fpm": 0,
"current_artcc": "ZNY"
},
"progress": {
"phase": "ENROUTE",
"is_active": true,
"distance_remaining_nm": 125.4,
"pct_complete": 95.2,
"time_to_dest_min": 15.7
},
"times": {
"eta": "2026-01-16T18:45:00Z",
"eta_runway": "2026-01-16T18:52:00Z",
"out": "2026-01-16T14:05:00Z",
"off": "2026-01-16T14:18:00Z"
},
"tmi": {
"is_controlled": false,
"ground_stop_held": false
}
}
],
"pagination": {
"total": 156,
"page": 1,
"per_page": 100,
"total_pages": 2,
"has_more": true
},
"timestamp": "2026-01-16T18:30:00Z"
}
Endpoint: GET /api/swim/v1/flight
Returns a single flight by GUFI or flight_key.
Query Parameters:
| Parameter | Type | Description |
gufi | string | Globally Unique Flight Identifier |
flight_key | string | ADL flight key |
format | string | legacy or fixm |
Endpoint: GET /api/swim/v1/positions
Returns bulk flight positions in GeoJSON FeatureCollection format.
Query Parameters:
| Parameter | Type | Description |
dept_icao | string | Departure airport filter |
dest_icao | string | Destination airport filter |
artcc | string | ARTCC filter |
bounds | string | Bounding box: minLon,minLat,maxLon,maxLat |
tmi_controlled | boolean | TMI-controlled flights only |
phase | string | Flight phase filter |
include_route | boolean | Include route string in properties |
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"id": 12345,
"geometry": {
"type": "Point",
"coordinates": [-74.5678, 40.1234, 35000]
},
"properties": {
"flight_uid": 12345,
"callsign": "UAL123",
"aircraft": "B738",
"departure": "KLAX",
"destination": "KJFK",
"phase": "ENROUTE",
"altitude": 35000,
"heading": 85,
"groundspeed": 480,
"distance_remaining_nm": 125.4,
"tmi_status": "none"
}
}
],
"metadata": {
"count": 2847,
"timestamp": "2026-01-16T18:30:00Z",
"source": "vatcscc"
}
}
Endpoint: GET /api/swim/v1/tmi/programs
Returns active Traffic Management Initiatives (Ground Stops, GDPs).
Query Parameters:
| Parameter | Type | Description |
type | string | all (default), gs, or gdp |
airport | string | Airport ICAO filter |
artcc | string | ARTCC filter |
include_history | boolean | Include recently ended programs |
{
"success": true,
"data": {
"ground_stops": [
{
"type": "ground_stop",
"airport": "KJFK",
"airport_name": "John F Kennedy Intl",
"artcc": "ZNY",
"reason": "Thunderstorms",
"probability_of_extension": 60,
"times": {
"start": "2026-01-16T17:00:00Z",
"end": "2026-01-16T19:00:00Z"
},
"is_active": true
}
],
"gdp_programs": [
{
"type": "gdp",
"program_id": "GDP_KEWR_20260116",
"airport": "KEWR",
"airport_name": "Newark Liberty Intl",
"artcc": "ZNY",
"reason": "Volume",
"rates": {"program_rate": 40},
"delays": {
"limit_minutes": 90,
"average_minutes": 45,
"maximum_minutes": 87
},
"flights": {
"total": 156,
"affected": 89
},
"is_active": true
}
],
"summary": {
"active_ground_stops": 1,
"active_gdp_programs": 1,
"total_controlled_airports": 2
}
}
}
Endpoint: GET /api/swim/v1/tmi/controlled
Returns flights currently under TMI control.
Endpoint: POST /api/swim/v1/ingest/adl
Receives flight data from authoritative sources. Requires write access (system or partner tier).
Maximum batch size: 500 flights per request
Request Body:
{
"flights": [
{
"callsign": "UAL123",
"dept_icao": "KJFK",
"dest_icao": "KLAX",
"cid": 1234567,
"aircraft_type": "B738",
"route": "DCT JFK J584 ORD J64 LAX",
"phase": "ENROUTE",
"is_active": true,
"latitude": 40.1234,
"longitude": -74.5678,
"altitude_ft": 35000,
"heading_deg": 270,
"groundspeed_kts": 450,
"vertical_rate_fpm": -500,
"out_utc": "2026-01-16T14:05:00Z",
"off_utc": "2026-01-16T14:18:00Z",
"eta_utc": "2026-01-16T18:45:00Z",
"tmi": {
"ctl_type": "GDP",
"slot_time_utc": "2026-01-16T18:30:00Z",
"delay_minutes": 45
}
}
]
}
Response:
{
"success": true,
"data": {
"processed": 1,
"created": 0,
"updated": 1,
"errors": 0,
"error_details": []
},
"timestamp": "2026-01-16T12:00:00Z",
"meta": {
"source": "vatcscc",
"batch_size": 1
}
}
Endpoint: POST /api/swim/v1/ingest/track
Receives real-time track/position updates from authoritative sources (vNAS, CRC, EuroScope, AOC systems).
Maximum batch size: 1000 tracks per request (higher limit for frequent position updates)
Request Body:
{
"tracks": [
{
"callsign": "UAL123",
"latitude": 40.6413,
"longitude": -73.7781,
"altitude_ft": 35000,
"ground_speed_kts": 450,
"heading_deg": 270,
"vertical_rate_fpm": -500,
"squawk": "1200",
"track_source": "radar"
}
]
}
Track Fields:
| Field | Type | Required | Description |
callsign | string | Yes | Aircraft callsign |
latitude | number | Yes | Latitude (-90 to 90) |
longitude | number | Yes | Longitude (-180 to 180) |
altitude_ft | integer | No | Altitude in feet MSL |
ground_speed_kts | integer | No | Ground speed in knots |
heading_deg | integer | No | Heading (0-360) |
vertical_rate_fpm | integer | No | Vertical rate (+ = climb, - = descend) |
squawk | string | No | Transponder code (4 digits) |
track_source | string | No | radar, ads-b, mlat, mode-s, acars |
timestamp | datetime | No | Observation time (ISO 8601) |
{
"success": true,
"data": {
"processed": 100,
"updated": 95,
"not_found": 5,
"errors": 0,
"error_details": []
},
"timestamp": "2026-01-16T12:00:00Z",
"meta": {
"source": "vnas",
"batch_size": 100
}
}
Notes:
Endpoint: GET /api/swim/v1/metering/{airport}
Returns TBFM-style metering data for arrivals to an airport.
Path Parameters:
| Parameter | Type | Description |
airport | string | Destination airport ICAO code (e.g., KJFK) |
| Parameter | Type | Description |
status | string | Filter by metering status: UNMETERED, METERED, FROZEN, SUSPENDED, EXEMPT |
runway | string | Filter by arrival runway (e.g., 31L) |
stream | string | Filter by arrival stream/corner post |
metered_only | boolean | Only return flights with metering data (default: true) |
format | string | Response format: json, fixm, xml, csv, ndjson |
{
"success": true,
"data": {
"airport": "KJFK",
"type": "metering_data",
"flights": [
{
"gufi": "VAT-20260116-UAL123-KORD-KJFK",
"callsign": "UAL123",
"sequence_number": 5,
"scheduled_time_of_arrival": "2026-01-16T18:30:00Z",
"metering_time": "2026-01-16T18:15:00Z",
"metering_delay": 5,
"metering_frozen": true,
"metering_status": "METERED",
"arrival_stream": "NORTH",
"arr_runway": "31L"
}
],
"count": 15,
"summary": {
"total": 15,
"metered": 12,
"frozen": 3,
"avg_delay_minutes": 4.2
}
}
}
Sequence Endpoint: GET /api/swim/v1/metering/{airport}/sequence
Returns a compact arrival sequence list sorted by sequence number, optimized for datablock display.
Endpoint: POST /api/swim/v1/ingest/metering
Receives TBFM-style metering data from authoritative sources (SimTraffic, vATCSCC).
Maximum batch size: 500 metering records per request
Request Body:
{
"airport": "KJFK",
"metering_point": "CAMRN",
"metering": [
{
"callsign": "UAL123",
"sequence_number": 5,
"scheduled_time_of_arrival": "2026-01-16T18:30:00Z",
"metering_time": "2026-01-16T18:15:00Z",
"metering_delay": 5,
"metering_frozen": true,
"arrival_stream": "NORTH",
"arrival_runway": "31L",
"metering_status": "METERED"
}
]
}
Response:
{
"success": true,
"data": {
"processed": 10,
"updated": 8,
"not_found": 2,
"errors": 0
},
"meta": {
"source": "simtraffic",
"airport": "KJFK",
"metering_point": "CAMRN"
}
}
Endpoint: GET /api/swim/v1/tmi/reroutes
Returns reroute definitions for Traffic Management Initiatives.
Query Parameters:
| Parameter | Type | Description |
status | string | Filter by status (comma-separated): 0=Draft, 1=Proposed, 2=Active, 3=Monitoring, 4=Expired, 5=Cancelled |
active | string | If 1, returns only active reroutes (status 1,2,3) |
limit | int | Maximum results (default: 100) |
offset | int | Pagination offset |
{
"status": "ok",
"total": 5,
"counts": {
"Active": 2,
"Proposed": 1,
"Monitoring": 2
},
"reroutes": [
{
"id": 1,
"status": 2,
"status_label": "Active",
"name": "KJFK_WX_REROUTE",
"adv_number": "ADV-2026-001",
"start_utc": "2026-01-16T14:00:00",
"end_utc": "2026-01-16T20:00:00",
"protected_fixes": "CAMRN,PARCH",
"avoid_fixes": "LENDY",
"dest_airports": "KJFK",
"dest_centers": "ZNY"
}
]
}
Endpoint: GET /api/swim/v1/tmi/measures
Returns ALL traffic management measures from both USA (vATCSCC) and external providers (ECFMP, NavCanada, VATPAC) in a unified TFMS/FIXM-aligned format.
Query Parameters:
| Parameter | Type | Description |
provider | string | Filter by provider: vATCSCC, ECFMP, etc. (comma-separated) |
type | string | Measure type: GS, GDP, AFP, MIT, MINIT, MDI, RATE, REROUTE |
airport | string | Control element filter (comma-separated ICAO codes) |
source | string | usa (vATCSCC only), external (non-USA), or all (default) |
active_only | boolean | Filter to active measures only (default: true) |
page | int | Page number (default: 1) |
per_page | int | Results per page (default: 100, max: 1000) |
curl -H "Authorization: Bearer swim_dev_test" \
"https://perti.vatcscc.org/api/swim/v1/tmi/measures?type=MIT,GS&active_only=true"
Example Response:
{
"success": true,
"data": {
"measures": [
{
"id": "USA-45",
"guid": "a1b2c3d4-...",
"provider": {
"code": "vATCSCC",
"name": "VATSIM Command Center (USA)"
},
"ident": "GS_KJFK_45",
"type": "GS",
"value": null,
"unit": null,
"controlElement": "KJFK",
"elementType": "APT",
"reason": "Thunderstorms",
"filters": {
"arrivalAerodrome": ["KJFK"]
},
"timeRange": {
"start": "2026-01-17T18:00:00Z",
"end": "2026-01-17T20:00:00Z"
},
"status": "ACTIVE",
"_source": "usa"
},
{
"id": "ECFMP-456",
"guid": "e5f6g7h8-...",
"provider": {
"code": "ECFMP",
"name": "EUROCONTROL Flow Management"
},
"ident": "EGTT22A",
"type": "MDI",
"value": 120,
"unit": "SEC",
"reason": "CTP Event Traffic",
"event": {
"id": 123,
"code": "CTP2026",
"name": "Cross the Pond 2026"
},
"filters": {
"departureAerodrome": ["KJFK", "KEWR"],
"arrivalAerodrome": ["EGLL", "EGKK"]
},
"exemptions": {
"eventFlights": true
},
"timeRange": {
"start": "2026-03-15T12:00:00Z",
"end": "2026-03-15T20:00:00Z"
},
"status": "ACTIVE",
"_source": "external"
}
],
"statistics": {
"by_provider": { "vATCSCC": 5, "ECFMP": 3 },
"by_type": { "GS": 2, "MIT": 3, "MDI": 2, "GDP": 1 },
"by_source": { "usa": 5, "external": 3 }
},
"pagination": {
"total": 8,
"page": 1,
"per_page": 100
}
}
}
Provider-agnostic integration for external flow management systems. Supports ECFMP (Europe/NAT), NavCanada, VATPAC, and future regional providers.
Endpoint: GET /api/swim/v1/tmi/flow/
Returns overview of external flow management endpoints and active counts.
Endpoint: GET /api/swim/v1/tmi/flow/providers
Returns registered external flow management providers.
Query Parameters:
| Parameter | Type | Description |
provider | string | Filter by provider code |
region | string | Filter by region: EUR, NAM, NAT, PAC |
active_only | boolean | Filter to active providers only (default: true) |
{
"success": true,
"data": {
"providers": [
{
"id": 1,
"code": "ECFMP",
"name": "EUROCONTROL Flow Management",
"api": {
"base_url": "https://ecfmp.vatsim.net/api/v1",
"version": "v1"
},
"coverage": {
"regions": ["EUR", "NAT"],
"firs": ["EGTT", "EGPX", "CZQX"]
},
"sync": {
"enabled": true,
"interval_sec": 300,
"last_sync_utc": "2026-01-17T15:30:00Z",
"last_status": "SUCCESS"
},
"is_active": true
}
]
}
}
Endpoint: GET /api/swim/v1/tmi/flow/events
Returns special events (CTP, FNO, etc.) from external providers.
Query Parameters:
| Parameter | Type | Description |
provider | string | Filter by provider: ECFMP, NAVCAN, etc. |
code | string | Event code filter: CTP2026, FNO2026 |
status | string | Status filter: SCHEDULED, ACTIVE, COMPLETED |
include_participants | boolean | Include participant list (default: false) |
{
"success": true,
"data": {
"events": [
{
"id": 123,
"guid": "abc123...",
"provider": { "code": "ECFMP", "name": "EUROCONTROL Flow Management" },
"code": "CTP2026",
"name": "Cross the Pond 2026",
"type": "SPECIAL",
"firs": ["EGTT", "EGPX", "CZQX", "KZNY"],
"timeRange": {
"start": "2026-03-15T12:00:00Z",
"end": "2026-03-15T20:00:00Z"
},
"exemptions": {
"groundStop": true,
"gdpPriority": true
},
"status": "SCHEDULED",
"participantCount": 1247
}
]
}
}
Endpoint: GET /api/swim/v1/tmi/flow/measures
Returns flow measures (MIT, MINIT, MDI, etc.) from external providers only.
Query Parameters:
| Parameter | Type | Description |
provider | string | Filter by provider |
type | string | Measure type: MIT, MINIT, MDI, RATE, GS, REROUTE |
event_id | int | Filter by associated event |
airport | string | Filter by control element |
active_only | boolean | Filter to active measures (default: true) |
| Type | Description | Unit |
MIT | Miles-In-Trail | NM |
MINIT | Minutes-In-Trail | MIN |
MDI | Minimum Departure Interval | SEC |
RATE | Departure Rate Cap | PER_HOUR |
GDP | Ground Delay Program | MIN |
AFP | Airspace Flow Program | MIN |
{
"success": true,
"data": {
"measures": [
{
"id": 456,
"ident": "EGTT22A",
"provider": { "code": "ECFMP" },
"type": "MDI",
"value": 120,
"unit": "SEC",
"event": { "id": 123, "code": "CTP2026" },
"reason": "CTP Event Traffic",
"filters": {
"departureAerodrome": ["KJFK", "KEWR", "KLGA"],
"arrivalAerodrome": ["EGLL", "EGKK"]
},
"exemptions": {
"eventFlights": true
},
"mandatoryRoute": ["KJFK", "MERIT", "NAT-A", "EGLL"],
"timeRange": {
"start": "2026-03-15T12:00:00Z",
"end": "2026-03-15T20:00:00Z"
},
"status": "ACTIVE"
}
]
}
}
Endpoint: GET /api/swim/v1/jatoc/incidents
Returns JATOC (Joint Air Traffic Operations Center) incident records.
Query Parameters:
| Parameter | Type | Description |
lifecycle_status | string | Filter: OPEN, IN_PROGRESS, RESOLVED, CLOSED |
facility | string | Filter by facility (partial match) |
facilities | string | Filter by multiple facilities (comma-separated, exact match) |
facility_type | string | Filter: ARTCC, TRACON, ATCT, FSS |
limit | int | Results per page (default: 50, max: 100) |
offset | int | Pagination offset |
{
"success": true,
"data": [
{
"id": 1,
"incident_number": "2026-001",
"facility": "KJFK",
"facility_type": "ATCT",
"incident_type": "EQUIPMENT",
"lifecycle_status": "RESOLVED",
"severity": "MEDIUM",
"summary": "Primary radar outage",
"start_utc": "2026-01-16T14:00:00",
"end_utc": "2026-01-16T15:30:00"
}
],
"pagination": {
"total": 45,
"limit": 50,
"offset": 0,
"page": 1
}
}
Endpoint: GET /api/swim/v1/splits/presets
Returns saved runway configuration presets.
Query Parameters:
| Parameter | Type | Description |
artcc | string | Filter by ARTCC |
airport | string | Filter by airport |
id | int | Get single preset by ID |
{
"success": true,
"presets": [
{
"id": 1,
"name": "KJFK_ILS31L_VIS31R",
"artcc": "ZNY",
"airport": "KJFK",
"description": "ILS 31L arrivals, Visual 31R departures",
"is_default": false,
"positions": [
{"runway": "31L", "operation": "ARR", "is_primary": true, "rate": 40},
{"runway": "31R", "operation": "DEP", "is_primary": true, "rate": 45}
]
}
]
}
Connect to the WebSocket server with your API key:
const ws = new WebSocket('wss://perti.vatcscc.org/api/swim/v1/ws?api_key=YOUR_KEY');
Connection Response:
{
"type": "connected",
"data": {
"client_id": "c_abc123def456",
"server_time": "2026-01-16T18:30:00Z",
"version": "1.0.0"
}
}
Send a subscribe message to receive specific event types:
{
"action": "subscribe",
"channels": ["flight.departed", "flight.arrived", "tmi.issued"],
"filters": {
"airports": ["KJFK", "KLGA", "KEWR"],
"artccs": ["ZNY"]
}
}
Available Channels:
| Channel | Description |
flight.created | New pilot connected to network |
flight.departed | Aircraft wheels-up (OFF time set) |
flight.arrived | Aircraft wheels-down (IN time set) |
flight.deleted | Pilot disconnected |
flight.positions | Batched position updates |
flight. | All flight events |
tmi.issued | New Ground Stop/GDP created |
tmi.released | TMI ended/released |
tmi. | All TMI events |
system.heartbeat | Server keepalive (30s interval) |
| Filter | Type | Description |
airports | array | ICAO codes to filter by departure/destination |
artccs | array | ARTCC IDs to filter |
callsign_prefix | array | Callsign prefixes (e.g., ["UAL", "DAL"]) |
bbox | object | Geographic bounding box {north, south, east, west} |
flight.departed:
{
"type": "flight.departed",
"timestamp": "2026-01-16T18:30:15.123Z",
"data": {
"callsign": "UAL123",
"flight_uid": 12345,
"dep": "KLAX",
"arr": "KJFK",
"off_utc": "2026-01-16T18:30:00Z"
}
}
flight.arrived:
{
"type": "flight.arrived",
"timestamp": "2026-01-16T22:45:30.456Z",
"data": {
"callsign": "UAL123",
"flight_uid": 12345,
"dep": "KLAX",
"arr": "KJFK",
"in_utc": "2026-01-16T22:45:00Z"
}
}
flight.positions (batched):
{
"type": "flight.positions",
"timestamp": "2026-01-16T18:30:15.123Z",
"data": {
"count": 2847,
"positions": [
{
"callsign": "UAL123",
"flight_uid": 12345,
"latitude": 40.1234,
"longitude": -74.5678,
"altitude_ft": 35000,
"groundspeed_kts": 480,
"heading_deg": 85,
"vertical_rate_fpm": 0,
"current_artcc": "ZNY",
"dep": "KLAX",
"arr": "KJFK"
}
]
}
}
tmi.issued:
{
"type": "tmi.issued",
"timestamp": "2026-01-16T17:00:00.000Z",
"data": {
"program_id": "GS_KJFK_20260116",
"program_type": "GROUND_STOP",
"airport": "KJFK",
"start_time": "2026-01-16T17:00:00Z",
"end_time": "2026-01-16T19:00:00Z",
"reason": "Thunderstorms"
}
}
system.heartbeat:
{
"type": "system.heartbeat",
"timestamp": "2026-01-16T18:30:00Z",
"data": {
"connected_clients": 47,
"uptime_seconds": 86400
}
}
Ping:
{"action": "ping"}
Response: {"type": "pong", "timestamp": "..."}
Status:
{"action": "status"}
Returns current subscriptions and message counts.
Unsubscribe:
{
"action": "unsubscribe",
"channels": ["flight.positions"]
}
{
"type": "error",
"code": "AUTH_FAILED",
"message": "Invalid or missing API key"
}
Error Codes:
| Code | Description |
AUTH_FAILED | Invalid or expired API key |
CONNECTION_LIMIT | Tier connection limit reached |
RATE_LIMITED | Too many messages per second |
INVALID_JSON | Malformed JSON message |
INVALID_CHANNEL | Unknown channel name |
INVALID_FILTER | Invalid filter specification |
MESSAGE_TOO_LARGE | Message exceeds 64KB limit |
Format: VAT-YYYYMMDD-CALLSIGN-DEPT-DEST
Example: VAT-20260116-UAL123-KLAX-KJFK
Generation:
function swim_generate_gufi($callsign, $dept_icao, $dest_icao, $date = null) {
if ($date === null) $date = gmdate('Ymd');
return implode('-', ['VAT', $date, strtoupper($callsign),
strtoupper($dept_icao), strtoupper($dest_icao)]);
}
| Phase | Description |
PREFLIGHT | Connected, not yet departed |
DEPARTING | Taxiing for departure |
CLIMBING | Airborne, climbing |
ENROUTE | Cruise altitude |
DESCENDING | Descending to destination |
APPROACH | On approach |
LANDED | Wheels down, taxiing |
ARRIVED | At gate/parked |
| Type | Description |
GS | Ground Stop |
GDP | Ground Delay Program |
MIT | Miles-in-Trail |
MINIT | Minutes-in-Trail |
AFP | Airspace Flow Program |
Virtual Airlines and flight simulator integrations can push telemetry data via the ADL ingest endpoint.
OOOI Times (Out/Off/On/In):
| Field | Description | Source |
out_utc | Gate departure (pushback) | AOC/ACARS |
off_utc | Wheels up (takeoff) | AOC/ACARS |
on_utc | Wheels down (landing) | AOC/ACARS |
in_utc | Gate arrival | AOC/ACARS |
| Field | Description | Source |
eta_utc | Estimated time of arrival | FMC/AOC |
etd_utc | Estimated time of departure | FMC/AOC |
| Field | Description | Source |
vertical_rate_fpm | Climb/descent rate (ft/min) | Flight sim |
latitude | Current position | Flight sim |
longitude | Current position | Flight sim |
altitude_ft | Current altitude (MSL) | Flight sim |
heading_deg | Current heading | Flight sim |
groundspeed_kts | Ground speed | Flight sim |
{
"flights": [
{
"callsign": "VPA123",
"dept_icao": "KJFK",
"dest_icao": "KLAX",
"cid": 1234567,
"out_utc": "2026-01-16T14:05:00Z",
"off_utc": "2026-01-16T14:18:00Z",
"latitude": 40.1234,
"longitude": -98.5678,
"altitude_ft": 35000,
"groundspeed_kts": 485,
"vertical_rate_fpm": 0,
"eta_utc": "2026-01-16T18:45:00Z"
}
]
}
Data Authority Rules:
The telemetry data source must be authorized to write the field:
SIMULATOR source: Authoritative for telemetry fields, can overrideVIRTUAL_AIRLINE source: Authoritative for airline fieldsThe API supports both legacy and FIXM-aligned field names via the ?format=fixm parameter.
| Legacy Name | FIXM Name |
callsign | aircraft_identification |
departure | departure_aerodrome |
destination | arrival_aerodrome |
cruise_altitude | cruising_level |
heading | track |
ground_speed_kts | ground_speed |
altitude_ft | altitude |
phase | flight_status |
wake_category | wake_turbulence |
airline_icao | operator_icao |
out | actual_off_block_time |
off | actual_time_of_departure |
on | actual_landing_time |
in | actual_in_block_time |
cd PERTI/sdk/python
pip install -e .
Or install directly:
pip install websockets
from swim_client import SWIMClient
Create client
client = SWIMClient('swim_dev_your_key', debug=True)
Handle events with decorators
@client.on('connected')
def on_connected(info, timestamp):
print(f"Connected! Client ID: {info.client_id}")
@client.on('flight.departed')
def on_departure(event, timestamp):
print(f"{event.callsign} departed {event.dep}")
@client.on('flight.arrived')
def on_arrival(event, timestamp):
print(f"{event.callsign} arrived at {event.arr}")
@client.on('system.heartbeat')
def on_heartbeat(data, timestamp):
print(f"Heartbeat: {data.connected_clients} clients")
Subscribe to channels
client.subscribe([
'flight.departed',
'flight.arrived',
'system.heartbeat'
])
Run (blocking)
client.run()
# Subscribe with airport filter
client.subscribe(
channels=['flight.departed', 'flight.arrived'],
airports=['KJFK', 'KLGA', 'KEWR'],
artccs=['ZNY']
)
Subscribe with bounding box
client.subscribe(
channels=['flight.positions'],
bbox={'north': 42.0, 'south': 39.0, 'east': -72.0, 'west': -76.0}
)
import asyncio
from swim_client import SWIMClient
async def main():
client = SWIMClient('swim_dev_your_key')
@client.on('flight.departed')
def on_departure(event, timestamp):
print(f"{event.callsign} departed")
client.subscribe(['flight.departed'])
await client.connect()
await client.run_async()
asyncio.run(main())
| Parameter | Default | Description |
api_key | required | API key for authentication |
url | wss://perti.vatcscc.org/... | WebSocket URL |
reconnect | True | Auto-reconnect on disconnect |
reconnect_interval | 5.0 | Initial reconnect delay (seconds) |
max_reconnect_interval | 60.0 | Maximum reconnect delay |
ping_interval | 30.0 | Ping interval (seconds) |
debug | False | Enable debug logging |
Located at PERTI/load/swim_config.php
API Version:
define('SWIM_API_VERSION', '1.0.0');
define('SWIM_API_PREFIX', '/api/swim/v1');
Rate Limits:
$SWIM_RATE_LIMITS = [
'system' => 30000,
'partner' => 3000,
'developer' => 300,
'public' => 100
];
Key Prefixes:
$SWIM_KEY_PREFIXES = [
'system' => 'swim_sys_',
'partner' => 'swim_par_',
'developer' => 'swim_dev_',
'public' => 'swim_pub_'
];
Data Sources:
$SWIM_DATA_SOURCES = [
// Core sources
'VATSIM' => 'vatsim', // Identity and flight plans
'vATCSCC' => 'vatcscc', // ADL, TMI, demand
// Track/position sources
'VNAS' => 'vnas', // Track data, ATC automation
'CRC' => 'crc', // Track data, tags
'EUROSCOPE' => 'euroscope', // Track data
// ACARS sources
'ACARS' => 'acars', // Generic ACARS (OOOI times)
'HOPPIE' => 'hoppie', // Hoppie ACARS
// Metering sources
'SIMTRAFFIC' => 'simtraffic', // TBFM-style metering
'TOPSKY' => 'topsky', // TopSky EuroScope AMAN
// External sources
'SIMBRIEF' => 'simbrief', // OFP data
'SIMULATOR' => 'simulator', // Pilot sim telemetry
'VIRTUAL_AIRLINE' => 'virtual_airline', // VA AOC systems (schedules, CDM)
// Future
'VFDS' => 'vfds', // vFlightDataSystems
];
Source Priority Rankings (per FAA CDM spec):
| Data Type | Priority Order (highest first) |
| Track Position | vNAS → CRC → EuroScope → simulator → ACARS |
| OOOI Times | ACARS → Virtual Airline → simulator → vATCSCC |
| Schedule (STD/STA) | Virtual Airline → SimBrief → vATCSCC |
| Metering | SimTraffic → vATCSCC → vNAS → TopSky |
| General Times | SimTraffic → vATCSCC → vNAS → vFDS → SimBrief → simulator |
| Field | CDM Ref | Description | Authority |
lrtd_utc | T1 | Airline Runway Time of Departure | Virtual Airline |
lrta_utc | T2 | Airline Runway Time of Arrival | Virtual Airline |
lgtd_utc | T3 | Airline Gate Time of Departure | Virtual Airline |
lgta_utc | T4 | Airline Gate Time of Arrival | Virtual Airline |
ertd_utc | T7 | Earliest Runway Time of Departure | Virtual Airline |
erta_utc | T8 | Earliest Runway Time of Arrival | Virtual Airline |
out_utc | T13 | Actual Off-Block (AOBT) | ACARS/VA/sim |
off_utc | T11 | Actual Takeoff (ATOT) | ACARS/VA/sim |
on_utc | T12 | Actual Landing (ALDT) | ACARS/VA/sim |
in_utc | T14 | Actual In-Block (AIBT) | ACARS/VA/sim |
| Behavior | Description |
priority_based | Higher priority source always wins (OOOI times) |
immutable | Only authoritative source can write (TMI, schedules) |
variable | Accepts newer timestamps (ETAs, metering) |
monotonic | Rejects older timestamps (position data) |
$SWIM_CACHE_TTL = [
'flights_list' => 5,
'flight_single' => 3,
'positions' => 2,
'tmi_programs' => 10,
'stats' => 60
];
Pagination:
define('SWIM_DEFAULT_PAGE_SIZE', 100);
define('SWIM_MAX_PAGE_SIZE', 1000);
define('SWIM_GEOJSON_PRECISION', 5);
$config = [
'auth_enabled' => true,
'rate_limit_msg_per_sec' => 10,
'heartbeat_interval' => 30,
'max_message_size' => 65536,
'allowed_origins' => [''],
'debug' => false
];
Tier Connection Limits:
// WebSocket connection limits (unchanged)
$tierLimits = [
'public' => 5,
'developer' => 50,
'partner' => 500,
'system' => 10000
];
Stores API key credentials and permissions.
CREATE TABLE dbo.swim_api_keys (
id INT IDENTITY(1,1) PRIMARY KEY,
api_key NVARCHAR(64) NOT NULL UNIQUE,
tier NVARCHAR(20) NOT NULL, -- system, partner, developer, public
owner_name NVARCHAR(100) NOT NULL,
owner_email NVARCHAR(255),
source_id NVARCHAR(50) NULL,
can_write BIT NOT NULL DEFAULT 0,
allowed_sources NVARCHAR(MAX) NULL, -- JSON array
ip_whitelist NVARCHAR(MAX) NULL, -- JSON array
description NVARCHAR(500) NULL,
expires_at DATETIME2 NULL,
created_at DATETIME2 NOT NULL DEFAULT GETUTCDATE(),
last_used_at DATETIME2 NULL,
is_active BIT NOT NULL DEFAULT 1
);
Request logging for monitoring and debugging.
CREATE TABLE dbo.swim_audit_log (
id BIGINT IDENTITY(1,1) PRIMARY KEY,
api_key_id INT NULL,
endpoint NVARCHAR(255) NOT NULL,
method NVARCHAR(10) NOT NULL,
ip_address NVARCHAR(45) NOT NULL,
user_agent NVARCHAR(500) NULL,
response_status INT NULL,
response_time_ms INT NULL,
request_time DATETIME2 NOT NULL DEFAULT GETUTCDATE()
);
WebSocket subscription tracking.
CREATE TABLE dbo.swim_subscriptions (
id INT IDENTITY(1,1) PRIMARY KEY,
api_key_id INT NOT NULL,
connection_id NVARCHAR(64) NOT NULL,
channels NVARCHAR(MAX) NOT NULL, -- JSON array
filters NVARCHAR(MAX) NULL, -- JSON object
connected_at DATETIME2 NOT NULL DEFAULT GETUTCDATE(),
last_ping_at DATETIME2 NOT NULL DEFAULT GETUTCDATE(),
is_active BIT NOT NULL DEFAULT 1,
FOREIGN KEY (api_key_id) REFERENCES swim_api_keys(id)
);
Webhook registration for push notifications.
CREATE TABLE dbo.swim_webhook_endpoints (
id INT IDENTITY(1,1) PRIMARY KEY,
api_key_id INT NOT NULL,
endpoint_url NVARCHAR(500) NOT NULL,
events NVARCHAR(MAX) NOT NULL, -- JSON array
secret NVARCHAR(64) NOT NULL, -- HMAC signing secret
retry_count INT NOT NULL DEFAULT 3,
timeout_seconds INT NOT NULL DEFAULT 30,
last_delivery_at DATETIME2 NULL,
failure_count INT NOT NULL DEFAULT 0,
created_at DATETIME2 NOT NULL DEFAULT GETUTCDATE(),
is_active BIT NOT NULL DEFAULT 1,
FOREIGN KEY (api_key_id) REFERENCES swim_api_keys(id)
);
PERTI/
├── api/swim/v1/
│ ├── index.php # API index
│ ├── auth.php # Authentication middleware
│ ├── flights.php # Flights endpoint
│ ├── flight.php # Single flight endpoint
│ ├── positions.php # GeoJSON positions
│ ├── ingest/ # Data ingestion endpoints
│ │ ├── adl.php
│ │ └── track.php
│ ├── tmi/ # TMI endpoints
│ │ ├── programs.php
│ │ └── controlled.php
│ └── ws/ # WebSocket components
│ ├── WebSocketServer.php
│ ├── ClientConnection.php
│ ├── SubscriptionManager.php
│ └── swim-ws-client.js
├── scripts/
│ ├── swim_ws_server.php # WebSocket daemon
│ ├── swim_ws_events.php # Event detection
│ └── startup.sh # Azure startup script
├── sdk/python/
│ ├── swim_client/
│ │ ├── __init__.py
│ │ ├── client.py
│ │ └── events.py
│ └── examples/
│ ├── basic_example.py
│ └── airport_monitor.py
├── load/
│ └── swim_config.php # Configuration
└── database/migrations/swim/
├── 001_swim_tables.sql
└── 002_swim_api_database.sql
# Start daemon
nohup php /home/site/wwwroot/scripts/swim_ws_server.php --debug > /home/LogFiles/swim_ws.log 2>&1 &
Check status
tail -f /home/LogFiles/swim_ws.log
Restart
pkill -f swim_ws_server
rm -f /home/site/wwwroot/scripts/swim_ws.lock
nohup php /home/site/wwwroot/scripts/swim_ws_server.php --debug > /home/LogFiles/swim_ws.log 2>&1 &
In startup.sh:
# Enable proxy modules
a2enmod proxy proxy_http proxy_wstunnel
Add to Apache config
<Location /api/swim/v1/ws>
ProxyPass ws://localhost:8090/
ProxyPassReverse ws://localhost:8090/
</Location>
Check Connected Clients:
curl -H "Authorization: Bearer swim_sys_internal" \
"https://perti.vatcscc.org/api/swim/v1/ws/stats"
Audit Log Cleanup:
EXEC dbo.sp_Swim_CleanupAuditLog @days_to_keep = 90;
| Phase | Status | Progress |
| Phase 0: Infrastructure | ✅ COMPLETE | 100% |
| Phase 1: REST API | ✅ COMPLETE | 100% |
| Phase 2: WebSocket | ✅ COMPLETE | 100% |
| Phase 3: SDKs | ✅ COMPLETE | Python, C#, Java, JS |
SWIM_API) - $5/monthsdk/csharp/)sdk/java/)api/swim/v1/ws/swim-ws-client.js)| Feature | Priority | Notes |
| Message compression | Low | Performance optimization |
| Historical replay | Low | Past event retrieval |
| Metrics dashboard | Low | Usage tracking |
| Redis caching | Deferred | File IPC adequate |
Document generated from PERTI codebase analysis - January 2026*