Skip to main content
GeoPilot

API reference

A minimal reference for the GeoPilot public API — every endpoint below is mounted at /api/v1/public on your GeoPilot deployment's domain, authenticated by an API key rather than a session cookie.

How ED-269 and ED-318 differ, field by field: read the guide. →

Drone no-fly zone API: getting started →

How to convert ED-269 and ED-318 geozones to GeoJSON or KML →

Machine-readable: the OpenAPI 3.1 description →

Changelog

What changed in the public API, newest first. A change is marked Breaking only when an existing, correctly-formed request now behaves differently; anything else is additive or a fix.

Added

Conflict checks now say which sources they left out

POST /restrictions/intersect responses carry a new excludedSources array: each source not licensed for the API whose restrictions the shape also meets, with a count, but never the restrictions themselves. Previously a check over such a country returned total: 0, which read as clear. Each conflict also carries sourceAttribution, the source's credit line, which CC BY data requires you to show.

Added

OpenAPI description and API catalog published

The public API is now described as OpenAPI 3.1 at /api/v1/public/openapi.json, generated from the same schemas the API validates requests with, and listed in an RFC 9727 catalog at /.well-known/api-catalog. A liveness endpoint, GET /api/v1/public/health, answers without a key. No existing request behaves differently.

Fixed

ED-318 conversions corrected against two real feeds

The ED-318 codec had been built from the published schema with no real feed to check it against. It's now verified directly against live exports, and several field mappings were wrong: the authorization value's spelling, where altitude is carried (on the geometry, not the properties), and how a multi-volume zone's geometry is represented. Any conversion to or from ED-318 — ed269-ed318, kml-ed318, geojson-ed318, and their reverse directions — may return different, corrected output for a real ED-318 document compared to before this fix.

Getting a key

API access is a separate product from the map subscription, billed on its own and self-serve once your account holds it.

New here? Subscribe to API access

Want to try it before you buy, or need a custom rate limit? Apply for access

Already subscribed? Create a key from your profile — name it, choose only the scopes you need, and copy the raw key when it's shown, since it's shown exactly once. Manage API keys

Authentication

Every request carries your key as a bearer token.

Authorization: Bearer <your-api-key>

Two failure states are distinguished:

  • Missing, malformed, unknown, revoked or expired key — 401 Unauthorized.
  • A valid key whose owner's API access has lapsed (subscription cancelled or expired) — 403 Forbidden.
  • A valid key that does not carry the scope an endpoint requires — 403 Forbidden, naming the missing scope.

Scopes

Each key is minted with one or more scopes. A request needs the scope its endpoint requires, regardless of whether the account holds API access at all.

RESTRICTIONS_READ
Read airspace-restriction data — list, point, radius and single-restriction lookups.
CONFLICT_CHECK
Check a drawn area or route against active restrictions.
CONVERT_ED269
Convert between ED-269 and GeoJSON.
CONVERT_KML
Convert between KML and any other supported format.
CONVERT_ED318
Convert between ED-318 and any other supported format.

Rate limits

Each bucket below is counted per key over a rolling one-minute window. Every response carries X-RateLimit-Limit and X-RateLimit-Remaining; a 429 also carries Retry-After.

Conversion
/convert/ed269-to-geojson, /convert/geojson-to-ed269, /convert/:from-:to
60 requests per minute (default).
Restriction reads
/restrictions, /at, /near, /:id
60 requests and 200 MB per minute (default).
Conflict check
/restrictions/intersect
60 requests per minute (default).

Need a higher limit for a specific integration? Apply for access

Errors

Every failure — from every endpoint — is the same JSON shape (RFC 9457 "problem details"), so your client can handle one error path instead of guessing per endpoint.

{
  "type": "https://geopilot.space/problems/forbidden",
  "title": "Insufficient permissions",
  "status": 403,
  "detail": "This API key does not have the CONFLICT_CHECK scope.",
  "instance": "/api/v1/public/restrictions/intersect",
  "requestId": "b3f2b8b0-8b7a-4c9b-9c9a-2f6a7a2b6b34"
}

errors lists field-level validation failures and appears only on a 400 or 422.

400
The request body, query or parameters failed validation.
401
Missing, malformed, unknown, revoked or expired key.
403
The key's owner no longer has active API access, or the key lacks the scope the endpoint requires.
404
The requested resource does not exist, or is not visible to your key — see the licensing note above.
422
The submitted geometry is invalid or too complex to evaluate.
429
Rate limit exceeded. Retry after the number of seconds given in Retry-After.

Countries the API serves

The API only serves data from sources whose publishers permit commercial reuse and redistribution, so it covers fewer countries than the map. Elsewhere, restriction reads return nothing, and a conflict check lists the sources it left out under excludedSources rather than reporting the area as clear.

Served:
Australia, Finland, Luxembourg, Norway, Switzerland, United States
On the map only (reuse terms not established):
Latvia, Portugal, Romania

Endpoints

Restriction reads only return data from sources whose licence allows commercial use and redistribution. A source excluded on those grounds behaves as if it does not exist: GET /restrictions/:id returns 404 for it exactly as it would for an unknown id. The conflict check is the one exception: it names what it left out in excludedSources, so an excluded country is never reported as clear.

GET /api/v1/public/restrictions

Scope: RESTRICTIONS_READ

List restrictions, filterable by country, bounding box, type, effect and more.

curl "https://geopilot.space/api/v1/public/restrictions?country=FI" \
  -H "Authorization: Bearer $GEOPILOT_API_KEY"

Response

{
  "meta": {
    "freshness": {
      "state": "FRESH",
      "sources": [{ "sourceId": "fi-traficom-zones", "state": "FRESH", "lastSyncedAt": "2026-09-01T09:00:00.000Z" }],
      "generatedAt": "2026-09-01T12:00:00.000Z"
    },
    "pagination": { "limit": 1000, "nextCursor": null, "returned": 214, "truncated": false },
    "detail": "FULL",
    "attributions": ["Source: Finnish Transport and Communications Agency Traficom, licensed under CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/). Modified by GeoPilot: geometry simplified, zones classified."],
    "sources": [
      {
        "id": "fi-traficom-zones",
        "providerKey": "fi-traficom-zones",
        "name": "Traficom UAS geographical zones",
        "attribution": "Source: Finnish Transport and Communications Agency Traficom, licensed under CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/). Modified by GeoPilot: geometry simplified, zones classified.",
        "sourceTimestamp": "2026-09-01T06:00:00.000Z",
        "lastSyncedAt": "2026-09-01T09:00:00.000Z"
      }
    ],
    "authorities": [{ "id": "traficom", "name": "Traficom", "phone": null, "email": null, "website": null }],
    "evaluatedAt": "2026-09-01T12:00:00.000Z",
    "disclaimer": "GeoPilot is an independent planning tool and is not affiliated with or endorsed by any aviation authority. It shows data published by national authorities, which may be incomplete, out of date or simplified in processing, and it does not authorize any flight. Always check the authority's own publication and current NOTAMs before flying."
  },
  "geojson": {
    "type": "FeatureCollection",
    "features": [
      {
        "type": "Feature",
        "id": "3f1b2c4d-0000-4000-8000-000000000000",
        "geometry": { "type": "Polygon", "coordinates": [[[24.90,60.29],[25.00,60.29],[25.00,60.35],[24.90,60.35],[24.90,60.29]]] },
        "properties": {
          "id": "3f1b2c4d-0000-4000-8000-000000000000",
          "externalId": "EF-UAS-EXAMPLE",
          "countryIso2": "FI",
          "name": "Example aerodrome UAS zone",
          "description": null,
          "type": "RESTRICTED_AREA",
          "effect": "AUTHORIZATION_REQUIRED",
          "effectBasis": "PROVIDER_RULE",
          "status": "ACTIVE",
          "lowerLimit": { "meters": 0, "datum": "GND", "raw": "GND", "parsed": true },
          "upperLimit": { "meters": 1500, "datum": "AMSL", "raw": "1500 AMSL", "parsed": true },
          "activationStart": null,
          "activationEnd": null,
          "schedule": null,
          "activeNow": true,
          "sourceId": "fi-traficom-zones",
          "authorityId": "traficom",
          "firstSeenAt": "2025-01-15T00:00:00.000Z",
          "lastSeenAt": "2026-09-01T09:00:00.000Z"
        }
      }
    ]
  }
}

GET /api/v1/public/restrictions/at

Scope: RESTRICTIONS_READ

Every restriction in the API's data stacked at one point.

curl "https://geopilot.space/api/v1/public/restrictions/at?lat=60.32&lon=24.95" \
  -H "Authorization: Bearer $GEOPILOT_API_KEY"

Response

{
  "meta": { "…": "same shape as GET /restrictions, above" },
  "geojson": {
    "type": "FeatureCollection",
    "features": [
      {
        "type": "Feature",
        "id": "3f1b2c4d-0000-4000-8000-000000000000",
        "geometry": { "type": "Polygon", "coordinates": [[[24.90,60.29],[25.00,60.29],[25.00,60.35],[24.90,60.35],[24.90,60.29]]] },
        "properties": {
          "id": "3f1b2c4d-0000-4000-8000-000000000000",
          "externalId": "EF-UAS-EXAMPLE",
          "name": "Example aerodrome UAS zone",
          "type": "RESTRICTED_AREA",
          "effect": "AUTHORIZATION_REQUIRED",
          "effectBasis": "PROVIDER_RULE",
          "status": "ACTIVE",
          "activeNow": true,
          "sourceId": "fi-traficom-zones",
          "authorityId": "traficom"
        }
      }
    ]
  }
}

GET /api/v1/public/restrictions/near

Scope: RESTRICTIONS_READ

Restrictions within a radius of a point, nearest first.

curl "https://geopilot.space/api/v1/public/restrictions/near?lat=60.32&lon=24.95&radiusMeters=5000" \
  -H "Authorization: Bearer $GEOPILOT_API_KEY"

Response

{
  "meta": { "…": "same shape as GET /restrictions, above" },
  "geojson": {
    "type": "FeatureCollection",
    "features": [
      {
        "type": "Feature",
        "id": "3f1b2c4d-0000-4000-8000-000000000000",
        "geometry": { "type": "Polygon", "coordinates": [[[24.90,60.29],[25.00,60.29],[25.00,60.35],[24.90,60.35],[24.90,60.29]]] },
        "properties": {
          "id": "3f1b2c4d-0000-4000-8000-000000000000",
          "externalId": "EF-UAS-EXAMPLE",
          "name": "Example aerodrome UAS zone",
          "type": "RESTRICTED_AREA",
          "effect": "AUTHORIZATION_REQUIRED",
          "effectBasis": "PROVIDER_RULE",
          "status": "ACTIVE",
          "activeNow": true,
          "sourceId": "fi-traficom-zones",
          "authorityId": "traficom",
          "distanceMeters": 842.6
        }
      }
    ]
  }
}

GET /api/v1/public/restrictions/:id

Scope: RESTRICTIONS_READ

One restriction by id.

curl "https://geopilot.space/api/v1/public/restrictions/3f1b2c4d-0000-4000-8000-000000000000" \
  -H "Authorization: Bearer $GEOPILOT_API_KEY"

Response

{
  "type": "Feature",
  "id": "3f1b2c4d-0000-4000-8000-000000000000",
  "geometry": { "type": "Polygon", "coordinates": [[[24.90,60.29],[25.00,60.29],[25.00,60.35],[24.90,60.35],[24.90,60.29]]] },
  "properties": {
    "id": "3f1b2c4d-0000-4000-8000-000000000000",
    "externalId": "EF-UAS-EXAMPLE",
    "countryIso2": "FI",
    "name": "Example aerodrome UAS zone",
    "description": null,
    "type": "RESTRICTED_AREA",
    "effect": "AUTHORIZATION_REQUIRED",
    "effectBasis": "PROVIDER_RULE",
    "status": "ACTIVE",
    "lowerLimit": { "meters": 0, "datum": "GND", "raw": "GND", "parsed": true },
    "upperLimit": { "meters": 1500, "datum": "AMSL", "raw": "1500 AMSL", "parsed": true },
    "activationStart": null,
    "activationEnd": null,
    "schedule": null,
    "activeNow": true,
    "authority": { "id": "traficom", "name": "Traficom", "phone": null, "email": null, "website": null },
    "source": {
      "id": "fi-traficom-zones",
      "providerKey": "fi-traficom-zones",
      "name": "Traficom UAS geographical zones",
      "attribution": "Source: Finnish Transport and Communications Agency Traficom, licensed under CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/). Modified by GeoPilot: geometry simplified, zones classified.",
      "sourceTimestamp": "2026-09-01T06:00:00.000Z",
      "lastSyncedAt": "2026-09-01T09:00:00.000Z"
    },
    "raw": { "…": "the provider's original record, preserved verbatim" },
    "firstSeenAt": "2025-01-15T00:00:00.000Z",
    "lastSeenAt": "2026-09-01T09:00:00.000Z"
  }
}

POST /api/v1/public/restrictions/intersect

Scope: CONFLICT_CHECK

Check a polygon or route against active restrictions and report what it meets.

curl -X POST "https://geopilot.space/api/v1/public/restrictions/intersect" \
  -H "Authorization: Bearer $GEOPILOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "geometry": {
      "type": "Polygon",
      "coordinates": [[[24.90,60.29],[25.00,60.29],[25.00,60.35],[24.90,60.35],[24.90,60.29]]]
    }
  }'

Response

{
  "conflicts": [
    {
      "restrictionId": "3f1b2c4d-0000-4000-8000-000000000000",
      "externalId": "EF-UAS-EXAMPLE",
      "name": "Example aerodrome UAS zone",
      "type": "RESTRICTED_AREA",
      "effect": "AUTHORIZATION_REQUIRED",
      "effectBasis": "PROVIDER_RULE",
      "authorityName": "Traficom",
      "intersectionAreaM2": 482310.5,
      "intersectionRatio": 0.61,
      "activeAtQueryTime": true,
      "sourceName": "Traficom UAS geographical zones",
      "sourceAttribution": "Source: Finnish Transport and Communications Agency Traficom, licensed under CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/). Modified by GeoPilot: geometry simplified, zones classified."
    }
  ],
  "summary": { "total": 1, "prohibited": 0, "authorizationRequired": 1, "coordinationRequired": 0, "informationOnly": 0, "unknown": 0 },
  "evaluatedAt": "2026-09-01T12:00:00.000Z",
  "disclaimer": "GeoPilot is an independent planning tool and is not affiliated with or endorsed by any aviation authority. It shows data published by national authorities, which may be incomplete, out of date or simplified in processing, and it does not authorize any flight. Always check the authority's own publication and current NOTAMs before flying.",
  "excludedSources": []
}

POST /api/v1/public/convert/ed269-to-geojson

Scope: CONVERT_ED269

Convert a published ED-269 document to a GeoJSON FeatureCollection.

curl -X POST "https://geopilot.space/api/v1/public/convert/ed269-to-geojson" \
  -H "Authorization: Bearer $GEOPILOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "document": { "UASZoneVersion": { "...": "the published ED-269 document" } } }'

Response

{
  "geojson": {
    "type": "FeatureCollection",
    "features": [
      {
        "type": "Feature",
        "geometry": { "type": "Polygon", "coordinates": [[[26.05,44.40],[26.15,44.40],[26.15,44.47],[26.05,44.47],[26.05,44.40]]] },
        "properties": {
          "externalId": "ROU-001",
          "name": "Example zone",
          "description": null,
          "type": "OTHER",
          "effect": "PROHIBITED",
          "effectBasis": "PROVIDER_RULE",
          "lowerLimit": { "meters": 0, "datum": "AGL", "display": "0 m AGL" },
          "upperLimit": { "meters": 120, "datum": "AGL", "display": "120 m AGL" },
          "activationStart": null,
          "activationEnd": null,
          "schedule": null,
          "authorityHint": null
        }
      }
    ]
  },
  "errors": [],
  "totalFeatures": 1
}

POST /api/v1/public/convert/geojson-to-ed269

Scope: CONVERT_ED269

Convert a GeoJSON Feature or FeatureCollection to an ED-269 document.

curl -X POST "https://geopilot.space/api/v1/public/convert/geojson-to-ed269" \
  -H "Authorization: Bearer $GEOPILOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "Feature",
    "geometry": { "type": "Polygon", "coordinates": [[[26.05,44.40],[26.15,44.40],[26.15,44.47],[26.05,44.47],[26.05,44.40]]] },
    "properties": { "name": "Example zone", "effect": "PROHIBITED" }
  }'

Response

{
  "document": {
    "title": "GeoPilot ED-269 export",
    "description": "Generated by GeoPilot's GeoJSON → ED-269 conversion endpoint.",
    "features": [
      {
        "name": "Example zone",
        "type": "CUSTOMIZED",
        "restriction": "PROHIBITED",
        "applicability": [],
        "zoneAuthority": [],
        "geometry": [
          {
            "uomDimensions": "M",
            "horizontalProjection": {
              "type": "Polygon",
              "coordinates": [[[26.05,44.40],[26.15,44.40],[26.15,44.47],[26.05,44.47],[26.05,44.40]]]
            }
          }
        ]
      }
    ]
  },
  "warnings": []
}

Endpoints — /convert/:from-:to

Beyond the two ED-269⇄GeoJSON endpoints above, POST /convert/:from-:to converts between any two of ED-269, ED-318, KML and GeoJSON — twelve directed pairs in total, listed below with the scope(s) each requires. ed269-geojson and geojson-ed269 under this short URL behave identically to, and coexist with, the two dedicated endpoints above; both forms keep working.

fromtoscope(s) required
ed269ed318CONVERT_ED269 + CONVERT_ED318
ed269kmlCONVERT_ED269 + CONVERT_KML
ed269geojsonCONVERT_ED269
ed318ed269CONVERT_ED318 + CONVERT_ED269
ed318kmlCONVERT_ED318 + CONVERT_KML
ed318geojsonCONVERT_ED318
kmled269CONVERT_KML + CONVERT_ED269
kmled318CONVERT_KML + CONVERT_ED318
kmlgeojsonCONVERT_KML
geojsoned269CONVERT_ED269
geojsoned318CONVERT_ED318
geojsonkmlCONVERT_KML

POST /api/v1/public/convert/ed269-kml

Scope: CONVERT_ED269 + CONVERT_KML

Convert a published ED-269 document to a KML document.

curl -X POST "https://geopilot.space/api/v1/public/convert/ed269-kml" \
  -H "Authorization: Bearer $GEOPILOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "document": { "title": "t", "features": [ { "identifier": "ROU-001", "name": "Example zone", "restriction": "PROHIBITED", "geometry": [ { "uomDimensions": "M", "horizontalProjection": { "type": "Polygon", "coordinates": [[[26.05,44.40],[26.15,44.40],[26.15,44.47],[26.05,44.47],[26.05,44.40]]] } } ] } ] } }'

Response

{
  "document": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n  <Document>\n    <Placemark>\n      <name>Example zone</name>\n      <ExtendedData>\n        <Data name=\"effect\"><value>PROHIBITED</value></Data>\n      </ExtendedData>\n      <Polygon>\n        <altitudeMode>clampToGround</altitudeMode>\n        <outerBoundaryIs><LinearRing><coordinates>26.05,44.40,0 26.15,44.40,0 26.15,44.47,0 26.05,44.47,0 26.05,44.40,0</coordinates></LinearRing></outerBoundaryIs>\n      </Polygon>\n    </Placemark>\n  </Document>\n</kml>",
  "errors": [],
  "totalFeatures": 1,
  "warnings": []
}

POST /api/v1/public/convert/kml-ed318

Scope: CONVERT_KML + CONVERT_ED318

Convert a KML document to an ED-318 document.

curl -X POST "https://geopilot.space/api/v1/public/convert/kml-ed318" \
  -H "Authorization: Bearer $GEOPILOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "document": "<kml><Document><Placemark><name>Example zone</name><ExtendedData><Data name=\"effect\"><value>PROHIBITED</value></Data></ExtendedData><Polygon><outerBoundaryIs><LinearRing><coordinates>26.05,44.40,0 26.15,44.40,0 26.15,44.47,0 26.05,44.47,0 26.05,44.40,0</coordinates></LinearRing></outerBoundaryIs></Polygon></Placemark></Document></kml>" }'

Response

{
  "document": {
    "type": "FeatureCollection",
    "features": [
      {
        "type": "Feature",
        "geometry": { "type": "Polygon", "coordinates": [[[26.05,44.40],[26.15,44.40],[26.15,44.47],[26.05,44.47],[26.05,44.40]]] },
        "properties": {
          "name": "Example zone",
          "type": "CUSTOMIZED",
          "restriction": "PROHIBITED",
          "applicability": [],
          "zoneAuthority": [],
          "uomDimensions": "M"
        }
      }
    ]
  },
  "errors": [],
  "totalFeatures": 1,
  "warnings": []
}
GeoPilot

An independent drone-planning tool, built on the airspace restrictions national authorities publish, for the European countries it covers and the United States.

Data

  • Airspace data is published by the national authorities of each country covered. GeoPilot is not affiliated with or endorsed by any of them.
  • View sources and freshness

GeoPilot is an independent planning tool and is not affiliated with or endorsed by any aviation authority. It shows data published by national authorities, which may be incomplete, out of date or simplified in processing, and it does not authorize any flight. Always check the authority's own publication and current NOTAMs before flying.