Starting on version 12.1.1, X-Plane includes a built-in web server that provides a way for applications to communicate with a simulator instance on your machine via the http protocol, using a REST and Websockets API.
As of X-Plane 12.1.1, the supported operations are:
- Getting the list of available Datarefs (REST)
- Getting the count of available Datarefs (REST)
- Read or write a particular Dataref (REST)
- Subscribing to one or more Datarefs to get periodic updates (Websockets)
- Streaming data to one or more Datarefs (Websockets)
We have plans to extend this further to handle Commands and other operations. See “Roadmap” at the end of this document.
The web server
The X-Plane web server endpoints are available at:
http://localhost:8086/api/v1/...
(for REST requests)ws://localhost:8086/api/v1
(for Websocket requests)
They share the same TCP port, because http and Websocket are application-level protocols, not server-level protocols.
You can pick the port to connect by launching X-Plane from the command line with the --web_server_port=
flag. Example: ./X-Plane --web_server_port=8088
.
If you wish to not expose your X-Plane instance to REST/Websocket traffic, effectively disabling the APIs entirely, you can set the network’s security policy in the ‘Network’ tab of the ‘Settings’ screen by selecting “Disable Incoming Traffic”.
Request format
For REST requests
The following headers are needed:
Accept: application/json
Content-Type: application/json
Body payload (if applicable) must be sent as a JSON formatted string (not FormData).
Example requests:
curl 'http://localhost:8086/api/v1/datarefs' \ -H 'Accept: application/json, text/plain, */*'
curl 'http://localhost:8086/api/v1/datarefs/1253033683792/value' \ -X 'PATCH' \ -H 'Accept: application/json, text/plain, */*' \ -H 'Content-Type: application/json' \ --data-raw '{"data":3}'
When a successful request happens, the server will return a HTTP 200 code and a payload. The payload is either null
for an operation that doesn’t return a result or an Object with a data
member, which will contain the requested object, observing the corresponding schema (see “Data schemas” below). If there’s a collection of objects, data
will contain a array of them.
Example “success” response:
{ "data": <dataref> } // or { "data": [<dataref>, <dataref>, <dataref>, ...] }
In case of an error, the corresponding HTTP error code will be returned, along with a payload containing a machine readable error code and a human readable error message. The possible error codes for each API method are detailed under the corresponding documentation for that method.
Note: If you have selected “Disable Incoming Traffic” on the ‘Settings’ screen, all methods will return a “403 Forbidden” HTTP error.
Example “error” response:
{ "error_code": "index_out_of_range", "error_message": "Index is out of range" }
For Websocket requests
Websocket messages must be sent as JSON formatted plain strings, and have the following shape:
{ "req_id": 123, "type": "...", "params": {...}, }
Where:
req_id
(required) is a Numeric, unique, non-recyclable identifier of the request. The response to the request will contain the same id. Since requests (messages) sent from the client to the server and responses (messages) received from the server are completely asynchronous, this id will prove useful to determine which response belongs to which request.type
(required) is a String identifying the operation to perform on the server. If the operation type is not recognized by the server, you’ll get theunknown_type
error code.params
(optional) is an Object containing the parameters of the operation.
For each valid message you send to the server, the server will send back an object that represents a message of type
”result”, with the success/failure of the operation indicated on the success
field, the req_id
which will match the one previously sent by the client in the request, and any errors on the error_code
and error_message
fields.
Example “success” result message:
{ "req_id": 123, // id of the corresponding request "type": "result", "success": true, }
Example “error” result message:
{ "req_id": 123, // id of the corresponding request "type": "result", "success": false, "error_code": "index_out_of_range", "error_message": "Index is out of range" }
For more information on how to handle websocket requests, see this MDN article.
Data schemas
Dataref
For now, the only schema available is <dataref>
. Every time you ask for a dataref, you’ll get an object with the following shape:
{ "id": 9952311, "name": "sim/cockpit2/gauges/actuators/radio_altimeter_bug_ft_pilot", "value_type": "float", // float, double, int, int_array, float_array, data }
id
is a numeric identifier of the dataref for the current session of the simulator. This id will be the way of referring to this dataref if you want to get its value or subscribe to updates. There’s no guarantee that this id will be the same the next time you start X-Plane, so don’t rely on it across simulator sessions but within an X-Plane session, even across aircraft loads and unloads it will be stable.name
the fully qualified name of the dataref, as used by the simulator and plugins.value_type
one offloat
,double
,int
,int_array
,float_array
ordata
(represented as base64 encoded strings. You must encode the data before sending it and decode it when receiving it).
Note: We have plans to extend this schema to support more data we know about the dataref, such as a human readable description, the dimension (for arrays), read/write status, etc.
REST API
List datarefs
GET /datarefs
Returns all registered datarefs in X-Plane at the moment of the request (built-in and third parties).
Query params:
filter[{field}]
(string, repeatable, optional) Specify to filter the dataref list by one or more fields using exact match. Currently the only supported field isname
. You can repeat the filters as much times as you need — same filters will be joined by “or” logic, different filters will be joined by “and” logic. Example?filter[name]=foo&filter[name]=bar&filter[name]=boo&filter[status]=CURRENT
means ‘name is (“foo” OR “bar” OR “boo”) AND status is “current”’.start
(int, optional) Index from where to start, inclusive (for pagination).limit
(int, optional) How many results to return (for pagination).fields
(string, optional) Comma separated list of fields to return for each record. You can use this to reduce the data transferred down the wire if you don’t need everything about the dataref:all
(default) or a comma separated list of field names as per the Dataref schema.
Note: For now, the only available fields are id
, name
, value_type
.
Success response:
An array of <dataref>
(see schema above)
{ "data": [<dataref>, <dataref>, <dataref>, ...] }
Possible errors:
HTTP code | error_code |
error_message |
---|---|---|
400 | start_out_of_range | Start out of range |
400 | limit_out_of_range | Limit out of range |
400 | invalid_field | Field {field} does not exist |
404 | invalid_dataref_name | Dataref {name} doesn’t exist |
Get dataref count
GET /datarefs/count
Returns the amount of registered datarefs in X-Plane at the moment (built-in and third parties).
Query params: (None)
Success response:
An object containing a data
member with the dataref count.
{ "data": 9554 }
Possible errors: (None)
Get Dataref value
GET /datarefs/{id}/value
Returns the current value of a dataref or the value of a member of an array dataref.
Url params:
id
(int, required) Id of the dataref to get
Query params:
index
(int, optional) index to get on array datarefs.
Success response:
An array of <dataref>
{ "data": [<dataref>, <dataref>, <dataref>, ...] }
Possible errors:
HTTP code | error_code |
error_message |
---|---|---|
400 | start_out_of_range | Start out of range |
400 | limit_out_of_range | Limit out of range |
400 | invalid_field | Field {field} does not exist |
404 | invalid_dataref_name | Dataref {name} doesn’t exist |
Set dataref value
PATCH /datarefs/{id}/value
Set a dataref value. In case of arrays, you can set a specific index of the array at a time, or you can set the whole array at once, but all values must be provided.
Url params:
id
(int, required) Id of the dataref to update
Query params:
index
(int, optional) index to set on array datarefs.
Body:
data
A single item or an array of: <Number>
; or a base64 encoded <String>
{ "data": 210 } // or { "data": [210, 220, 240] }
Success response:
Just the HTTP code 200 OK. (Empty response)
Possible errors:
HTTP code | error_code |
error_message |
---|---|---|
400 | index_out_of_range | Dataref array index out of range |
400 | not_an_array | An index was provided but the dataref is not an array |
400 | incompatible_data | Provided data is too much or not enough to set all elements of the dataref array, or sent an array to write to a non-array dataref. |
400 | invalid_body | The request body is not valid JSON |
403 | dataref_is_readonly | Attempted to write to a read-only dataref |
404 | invalid_dataref_id | Dataref {id} doesn’t exist |
Websockets API
Subscribe to dataref value updates
Receives an array of dataref ids to subscribe to. It adds to the list of already subscribed datarefs. The operation is idempotent. A failed subscription event fails all datarefs sent.
Request message:
Send a dataref_subscribe_values
message with the list of datarefs you wish to subscribe as an array of objects containing id
with the dataref id and optionally index
with a index or array of indices you’re interested in getting back.
{ "req_id": 9998, // Unique key to know the result later "type": "dataref_subscribe_values", "params": { "datarefs": [ // only interested in idx 2 of an array dataref { "id": 1223, "name": "sim/bla/...", "index": 2 }, // only interested in idx 0 to 3 of an array dataref { "id": 1224, "index": [0,1,2,3] }, // single value dataref, or return all elements of an array dataref { "id": 1225 } ] } }
Note: If you subscribed to certain indexes of the dataref, they’ll be sent in the index order, but no sparse arrays will be sent. For example if you subscribed to indexes [1, 5, 7]
you’ll get a 3 item array like [200, 200, 200]
, meaning you need to remember that the first item of that response corresponds to index 1
, the second to index 5
and the third to index 7
of the dataref. This also means that if you subscribe to index 2
and later to index 0
you’ll get them as [0,2]
. So bottom line is — keep it simple: either ask for a single index, or a range, or all; and if later your requirements change, unsubscribe, then subscribe again.
Success message:
If the operation succeeded, the server will send a result
message indicating so.
{ "req_id": 9998, // id of the corresponding request operation "type": "result", "success": true }
Update messages:
After a successful subscription, the server will start to push periodically (currently at 10Hz) to the client the following message of type dataref_update_values
, containing an object with the dataref id as key and the dataref value as value:
{ "type": "dataref_update_values", "data": { "88491": 0, "3994": 5, "199 ": [0, 0, 0, 4], ... } }
Note: The message will contain only the datarefs whose values have changed since the last time they were sent. This means the first dataref_update_values
message after a subscription will contain the values of all subscribed datarefs, but subsequent messages will only contain the updates.
Possible errors:
error_code |
error_message |
---|---|
index_out_of_range | Dataref {id} array index out of range |
not_an_array | An index was provided for dataref {id} but the dataref is not an array |
invalid_dataref_id | Dataref {id} doesn’t exist |
Unsubscribe to dataref value updates
Receives an array of datarefs to stop listening to. It removes them from the list of already subscribed datarefs. The operation is idempotent. If the dataref was not on the list, it’s ignored.
Request message:
Send a dataref_unsubscribe_values
message with the list of datarefs you wish to unsubscribe as an array of objects containing id
with the dataref id and optionally index
with a index or array of indices you’re interested in unsubscribing to.
Instead of an array of datarefs, you could also send the special string "all"
if you want to unsubscribe from every dataref you have subscribed.
{ "req_id": 1234, // Unique key to know the result later "type": "dataref_unsubscribe_values", "params": { "datarefs": [ // only unsubscribe to idx 2 of the array dataref { id: 1223, index: 2 }, // only unsubscribe to idx 0 to 3 of the array dataref { id: 1224, index: [0,1,2,3] }, // unsubscribe from a single value dataref, // or from all indices of an array dataref { id: 1225 } ] } // or "params": { "datarefs": "all" } }
Success message:
The server will return the status of the operation. No further update messages will be received on the client.
{ "req_id": 1234, // id of the corresponding request operation "type": "result", "success": true }
Possible errors:
error_code |
error_message |
---|---|
index_out_of_range | Dataref {id} array index out of range |
not_an_array | An index was provided for dataref {id} but the dataref is not an array |
invalid_dataref_id | Dataref {id} doesn’t exist |
Set dataref values
Set one or more dataref values. In case of array datarefs, you can set a specific index of the array at a time, or you can set the whole array at once, but all values must be provided.
Request message:
Send a dataref_set_values
message with the list of the dataref ids to update. The list must contain objects with id
for the dataref id, value
for the value to set (according to the dataref type) and optionally index
to set a single index on an array dataref.
{ "req_id": 1234, // Unique key to know the result later "type": "dataref_set_values", "params": { "datarefs": [ { "id": 88491, "value": 6 }, { "id": 39222, "value": [2, 2, 1, 0] }, { "id": 37555, "value": 2, "index": 1 }, ... ] } }
Success message:
The server will return the status of the operation.
{ "req_id": 1234, // id of the corresponding request operation "type": "result", "success": true }
Possible errors:
If one or more update operations failed on the server, you’ll receive as many error messages as failed operations.
error_code |
error_message |
---|---|
index_out_of_range | Dataref {id} array index out of range |
not_an_array | An index was provided for dataref {id} but the dataref is not an array |
insufficient_data | Provided data for dataref {id} is not enough to set all elements of the dataref array |
invalid_dataref_id | Dataref {id} doesn’t exist |
Roadmap
The end goal of this API is that you’re able to remotely control the simulator from another machine on the network. With that in mind, we plan to implement the following features, but we’re open to community feedback if you have more ideas.
- Authorization: Allow clients to ask X-Plane for authorization to connect. X-Plane will present an authorization request popup asking the user if they want to allow the application to connect. The user will be able to see what applications are connected and revoke acesss to specific client apps. This will allow X-Plane to receive connections from other machines on the local area network.
- Subscribe to dataref list updates (Websocket): Receive a message when new datarefs are created by the sim.
- Commands (REST, Websocket): Similar APIs to the Datarefs, to invoke commands, read their status and get status updates.
- Aircraft (REST): Allow the client to read the available aircraft and their capabilities.
- Traffic (REST, Websockets): Allow the client to read/write and get updates on the user and AI aircraft positions.
- Flight initialization (REST): Allow the client to initialize the simulator specifying aircraft, location, weather, weight and balance, etc.
- Weather (REST, Websocket): Allow the client to read/write and get updates on weather.
- …and more (failures, situations, nav data, time, airports, etc)
I think it’s rather odd that after setting “Disable Incoming Traffic” you can still connect and receive an error response.
I would think that “Disable Incoming Traffic” actually does not open the listening socket, so one would get a “connection refused” when trying to connect.
You can use the command line –no_web_server to completely kill things off.
Fantastic!
Thank you!!!
Will this allow web based 2D instruments to build a home cockpit?
For example, PFD, ND, EICAS for the A320.
Those instruments, along with your superior multi-monitor support, would position X-Plane as a competitive alternative to MSFS for home cockpit builders.
Once the command part of the API is done, yes. Right now you could do displays that don’t need to _change_ info (e.g. glass non-touch-screens). But this only provides the data, not the _image_.
Thank you! Thank you! Thank you! This is exactly what I needed! Now I can build exactly what I need.
Does this allow connections from other computers on the local network? I’ve been trying to connect to port 8086 on my machine running XP12.1.1 and I’m unable to connect. I’ve turned Windows Defender off temporarily for testing purposes too.
Right now, no, we only listen on the local machine (unless the security setting in network config is “no incoming”, in which case we don’t init the server). We do expect to extend to remote machines in the future, but right now the connections are unauthenticated, so we’re being conservative about connectivity. We might provide a “promiscuous” setting (defaulted off) so devs can work before the full auth model is done.
Sorry, what I wrote is incorrect – I checked the code after reading another comment.
–no_web_server
means the web server is COMPLETELY off, not running, port never opened, nothing will work. Similar in consequence and difficulty as –disable_networking for UDP.
If the security policy is set to “no incoming” we just 403 everything.
In all cases, the server is only listening for local connections for now.
Trying to query the current altitude caused xplane to crash on my end.
/api/v1/datarefs/2636311144576/value
Hi Chad, could you please send in more detailed information via our bug reporter. We’ve checked and unable to determine specifically what you are referring to!
https://www.x-plane.com/x-plane-bug-report-form/