Give your AI coding tool a Geoapify OpenAPI specification, and it can work from the real endpoints, parameters, and response schemas instead of guessing. The same JSON file also imports into Postman or Insomnia and generates typed clients for your codebase. We tested this workflow with real Claude Code runs, TypeScript code generation, and live API calls, and this guide shows what worked and what still needs a human check.
Key takeaways
- One catalog:
index.jsonlists 34 Geoapify OpenAPI specifications, from Forward Geocoding to focused MCP tool specs. - Load only what you need: Forward Geocoding is about 60 KB, while Routing and Batch are about 245–250 KB each.
- Same file, three uses: API clients, typed code generation, and AI coding agents all read the same specification.
- OpenAPI for building, MCP for runtime: use specifications to write deterministic code and Geoapify MCP when an agent should call tools itself.
On this page
What Is an OpenAPI Specification?
An OpenAPI specification is a machine-readable description of an HTTP API: its servers, paths, operations, parameters, authentication methods, and response schemas. It follows the OpenAPI Specification standard, so tools such as Swagger UI, Postman, and code generators can read it without custom adapters.
For an AI coding tool, a specification works as a contract. Without it, the model relies on training data, which may be outdated or mix up similar APIs. With it, the model can see which parameters are required, which values an enum allows, what limits apply, and what the response looks like.
Here is a simplified excerpt of the real forwardGeocode operation from the Geoapify Forward Geocoding specification:
{
"openapi": "3.1.0",
"info": { "title": "Geoapify Forward Geocoding API", "version": "1.1.1" },
"servers": [
{ "url": "https://api.geoapify.com/v1", "description": "Default API endpoint." },
{ "url": "https://api-eu.geoapify.com/v1", "description": "EU-focused endpoint. Geoapify service processing uses EU infrastructure." }
],
"security": [{ "ApiKeyInQuery": [] }, { "ApiKeyInHeader": [] }],
"paths": {
"/geocode/search": {
"get": {
"operationId": "forwardGeocode",
"summary": "Forward geocode an address",
"parameters": [
{
"name": "text",
"in": "query",
"description": "Free-form address query. Alternative to the structured address parameters; do not combine them.",
"schema": { "type": "string", "minLength": 1, "maxLength": 1024 }
},
{ "name": "lang", "in": "query", "schema": { "type": "string", "enum": ["en", "de", "fr", "..."] } },
{ "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100 } },
{
"name": "format",
"in": "query",
"schema": { "type": "string", "enum": ["json", "geojson", "xml"], "default": "geojson" }
}
]
}
}
},
"components": {
"securitySchemes": {
"ApiKeyInQuery": { "type": "apiKey", "in": "query", "name": "apiKey" },
"ApiKeyInHeader": { "type": "apiKey", "in": "header", "name": "x-api-key" }
}
}
}Even this short excerpt answers questions an AI tool would otherwise guess: limit is an integer from 1 to 100, format defaults to geojson, text should not be combined with structured address fields, and the key can go in the query or in a header.
Which Geoapify OpenAPI Specifications Are Available?
Geoapify publishes OpenAPI 3.1.0 specifications for its location APIs and MCP tools. They are grouped as follows:
| Group | APIs |
|---|---|
| Geocoding | Address Autocomplete, Batch Geocoding, Forward Geocoding, Reverse Geocoding |
| Routing | Routing, Route Matrix, Route Planner, Map Matching, Isoline |
| Places and location data | Places, Place Details, Place Info, Boundaries, Postcode, IP Geolocation, Elevation |
| Maps and geometry | Map Tiles, Static Maps, Map Marker, Geometry, Geometry Operations |
| Request orchestration | Batch API |
| MCP | Complete protocol specification and focused tool specifications |
You can get the specifications in three places:
- Overview page: Geoapify OpenAPI specifications on the API documentation site.
- GitHub: the geoapify-openapi-specs repository, MIT-licensed, with a local Swagger UI and Spectral lint rules.
- Hosted explorer: a Swagger UI explorer on GitHub Pages for browsing without installing anything.
Each specification also has a stable URL that tools can fetch directly, for example https://apidocs.geoapify.com/assets/openapi/specs/forward-geocoding/forward-geocoding-api-openapi-specs.json.
Choosing the Right Specification with index.json
The catalog at index.json lists all 34 specifications. Each entry includes id, title, description, category, documentationUrl, openApiSpecUrl, openApiVersion, and apiVersion. A separate mcp/tools/index.json catalog describes the focused MCP tool specifications.
You can explore the catalog with curl and jq:
# List every specification with its download URL
curl -s https://apidocs.geoapify.com/assets/openapi/specs/index.json \
| jq -r '.apis[] | [.id, .title, .openApiSpecUrl] | @tsv'
# Find routing-related specifications
curl -s https://apidocs.geoapify.com/assets/openapi/specs/index.json \
| jq '.apis[] | select((.id + " " + .description) | test("route|routing"; "i")) | {id, title, openApiSpecUrl}'The second command returns five entries: the Routing, Route Matrix, and Route Planner APIs plus the calculate-route and calculate-route-matrix MCP tool specs. That matters because specification sizes vary a lot:
| Specification | Approximate size |
|---|---|
| Forward Geocoding | ~60 KB |
| Address Autocomplete | ~72 KB |
| Places | ~133 KB |
| Routing | ~245 KB |
| Batch | ~250 KB |
Size is not the same as relevance. When we analyzed the Routing specification, about 58% of its minified size was two worked examples, and 38 of its 60 schemas were not referenced by the /routing operation. Loading several large files "just in case" fills the AI tool's context with material it doesn't need.
The repository README gives AI agents four rules that work well as a default:
- Match the task to a catalog entry using
id,category, anddescription. - Load only the selected specification unless the task needs several APIs.
- Treat that specification as the source of truth for endpoints, parameters, authentication, and schemas.
- Never invent an endpoint or parameter that is absent from the specification.
Ready to pick a specification? Start from the catalog overview.
How Do You Import a Geoapify Specification into Postman or Insomnia?
API clients turn a specification into a ready-made collection of requests, which is the fastest way to try parameters before writing code. Both Postman and Insomnia accept an OpenAPI file or URL. In the examples below, we use the Routing API specification URL:
https://apidocs.geoapify.com/assets/openapi/specs/routing/routing-api-openapi-specs.jsonPostman
In Postman, use the Import action and paste the specification URL, or select a downloaded JSON file. Postman creates requests from the operations in the file. See Working with OpenAPI in Postman for the current import options.
After the import, the Calculate a route between waypoints request is ready to send. Every query parameter is pre-filled with an example value and described with text from the specification, and the documented responses (200, 400, 401, 429, and 500) are saved as examples under the request.
{{baseUrl}} variable; set it to one of the servers listed in the specification.Insomnia
Insomnia can also import an OpenAPI document from a URL or a file and create requests from it. Follow Kong's guide on importing an API spec into Insomnia.
Insomnia opens the specification as a document: the raw OpenAPI JSON on one side and a rendered preview with the servers, the GET /routing operation, and the schemas on the other. Its built-in linter also checks the file against an OpenAPI ruleset.
If you only want to read the operations and try requests without installing a client, open the hosted Swagger explorer.
Set Up Authentication and the EU Endpoint
After import, configure the API key and server once at the collection or environment level. Every Geoapify public specification declares two security schemes:
| Scheme | Where the key goes | Best for |
|---|---|---|
ApiKeyInQuery | ?apiKey=YOUR_API_KEY | Browser integrations where a header is not practical |
ApiKeyInHeader | x-api-key: YOUR_API_KEY | Server-to-server clients, because URLs get stored in logs, browser history, and analytics |
The specifications also list two servers: https://api.geoapify.com/v1 (default) and https://api-eu.geoapify.com/v1, an EU-focused endpoint where Geoapify service processing uses EU infrastructure. Pick the server in your API client's environment rather than editing each request. Create keys in the Geoapify Projects dashboard.
How Do You Generate a Typed TypeScript Client?
Generated types turn the specification into compile-time checks: wrong parameter types, unknown enum values, and misspelled paths fail before a request is sent. We used openapi-typescript to generate types and openapi-fetch as a small typed fetch wrapper.
Generate Types and Call the API
First, download the specification for the Geocoding API (Forward Geocoding) and generate the types:
curl -o forward-geocoding.json \
https://apidocs.geoapify.com/assets/openapi/specs/forward-geocoding/forward-geocoding-api-openapi-specs.json
npm install openapi-fetch
npm install -D openapi-typescript typescript
npx openapi-typescript forward-geocoding.json -o src/geoapify-geocoding.d.tsWith openapi-typescript 7.13.0, this produced an 807-line type file in about 40 ms. The client code then imports the generated paths type and reads the API key from an environment variable:
import createClient from "openapi-fetch";
import type { paths } from "./geoapify-geocoding";
const geoapify = createClient<paths>({
baseUrl: "https://api.geoapify.com/v1",
headers: { "x-api-key": process.env.GEOAPIFY_API_KEY! },
});
const { data, error } = await geoapify.GET("/geocode/search", {
params: {
query: {
text: "Unter den Linden 77, 10117 Berlin",
lang: "en",
limit: 1,
format: "json",
},
},
});
if (error) throw new Error(JSON.stringify(error));
console.log(data.results?.[0]);Simplified response:
{
"results": [
{
"name": "Adlon Kempinski",
"formatted": "Adlon Kempinski, Unter den Linden 77, 10117 Berlin, Germany",
"lat": 52.5155757,
"lon": 13.3800699,
"result_type": "amenity",
"category": "accommodation.hotel",
"rank": {
"confidence": 1,
"match_type": "inner_part"
}
}
],
"query": {
"text": "Unter den Linden 77, 10117 Berlin",
"parsed": {
"housenumber": "77",
"street": "unter den linden",
"postcode": "10117",
"city": "berlin",
"expected_type": "building"
}
}
}The address matched the Hotel Adlon building with confidence 1. See the Forward Geocoding documentation for the full list of response fields.
What the Generated Types Catch
We deliberately introduced mistakes and ran tsc. These are the real compiler messages:
| Mistake | TypeScript result |
|---|---|
limit: "5" | Type 'string' is not assignable to type 'number'. |
lang: "german" | Type '"german"' is not assignable to type ..., followed by the list of allowed lang codes |
geoapify.GET("/geocode/serch", ...) | Argument of type '"/geocode/serch"' is not assignable to parameter of type '"/geocode/search"'. |
language: "de" (unknown key) | No error |
The last row is worth remembering. With openapi-fetch 0.17.0, an extra query key that the specification doesn't declare passed type-checking silently, so the request would be sent with a parameter the API ignores. Verify this behavior in your own setup, and add a lint rule or a review step for unknown parameters.
Alternatives and Practical Tips
If you prefer a full generated SDK with model classes, OpenAPI Generator offers a typescript-fetch generator:
npx @openapitools/openapi-generator-cli generate \
-i forward-geocoding.json \
-g typescript-fetch \
-o generated-clientThe npm wrapper still runs the Java-based generator, so it needs Java 11 or newer (see the installation guide).
One practical tip for larger specifications: openapi-typescript output for the Places specification failed tsc with error TS2502 because of the recursive PlaceDetailValue type. Setting "skipLibCheck": true in tsconfig.json worked around it, since the generated file is a .d.ts declaration file.
How Do You Give a Specification to Claude Code, Cursor, Copilot, or Codex?
The most reliable approach works the same way in every tool: download the specification into your repository, reference it in your prompt, and add a persistent instruction file so the tool follows the same rules in every session. A local file is versioned with your code and doesn't depend on the tool's web access.
| Tool | Persistent instructions | Official docs |
|---|---|---|
| Claude Code | CLAUDE.md memory file in the project | Claude Code memory |
| Cursor | Project rules | Cursor rules |
| GitHub Copilot | .github/copilot-instructions.md | Repository custom instructions |
| Codex | AGENTS.md | Codex AGENTS.md, agents.md |
A simple layout keeps only the specifications your project uses:
mkdir -p specs
BASE=https://apidocs.geoapify.com/assets/openapi/specs
curl -o specs/forward-geocoding.json $BASE/forward-geocoding/forward-geocoding-api-openapi-specs.json
curl -o specs/routing.json $BASE/routing/routing-api-openapi-specs.json
curl -o specs/places.json $BASE/places/places-api-openapi-specs.jsonThen add a short, ready-to-copy instruction block to AGENTS.md, CLAUDE.md, a Cursor rule, or .github/copilot-instructions.md:
## Geoapify API integration
- Use the OpenAPI specifications in ./specs as the source of truth for
Geoapify endpoints, parameters, authentication, and response schemas.
- Load only the specification needed for the current task.
- Never invent endpoints or parameters that are not in the specification.
- Read the API key from the GEOAPIFY_API_KEY environment variable and send it
in the x-api-key header. Never hard-code or log the key.
- Verify coordinate order for every location string: filters, bias, and
GeoJSON use lon,lat; Routing API waypoints use lat,lon.
- Check units (meters, seconds) and defaults such as limit before relying on results.
Which Prompts Work Well for Geoapify Integrations?
We ran four prompts in Claude Code, each in a scratch project with the relevant specifications in ./specs. Below are the prompts, ready to copy, and what actually happened.
Prompt 1: A Typed Geocoding Client
This prompt asks for a small, strictly typed wrapper around Forward Geocoding:
Using ./specs/forward-geocoding.json, create a typed TypeScript client with
geocodeAddress(text, options). Support only options declared in the spec.
Read the API key from GEOAPIFY_API_KEY and send it in the x-api-key header.
Request format=json. Test it with "Unter den Linden 77, 10117 Berlin".What happened in our test:
- The agent generated types with openapi-typescript and wrote a hand-written
fetchclient.tscpassed with no errors. - The live test returned the correct result: Adlon Kempinski, confidence
1,match_typeinner_part,result_typeamenity. - It left structured address fields out of
geocodeAddressbecause the specification says not to combine them withtext. - The specification doesn't declare how array parameters such as
countrycodes,filter, andbiasare serialized, so the agent probed the live API. The Geocoding documentation answers this: combine multiple filters in one parameter separated by|(AND logic) and pass country codes comma-separated, such asfilter=countrycode:de,es,fr. - The live response included a
refproperty that is not declared in the response schema.
Prompt 2: An Address Search Component
The second prompt targets a front-end feature based on the Address Autocomplete API:
Using ./specs/address-autocomplete.json, build a framework-free TypeScript
address search component. Debounce input, cancel outdated requests, and let
the caller restrict countries and choose a language using values from the spec.What happened in our test:
- The result type-checked and bundled to 7.4 KB. Enums from the specification turned bad values such as
lang: "xx"orcountryCodes: ["DE"](uppercase) into compile errors. - It used a 300 ms debounce, a 3-character minimum, and
AbortController. In a simulated slow-network test, typing produced 9 requests; 6 were cancelled and only the latest result was shown. - The agent chose
format=jsonso the UI readslatandlonproperties instead of GeoJSON[lon, lat]arrays. - Ranking needed a product decision. The query "Unter den Lin" limited to Germany returned streets in Jüchen, Dortmund, Duisburg, and other cities before Berlin. Adding
bias=proximity:13.3888,52.5170(lon,lat) moved Berlin's Unter den Linden to the top. - A browser component exposes the key, so use a key restricted to your domain in the Geoapify dashboard or call the API through a backend proxy. See the Address Autocomplete documentation for parameters.
Prompt 3: Explain Routing Parameters and Response Types
Not every prompt needs to produce code. Asking the agent to explain a specification is a quick way to learn an API like the Routing API:
Read ./specs/routing.json and explain which parameters are required for
GET /routing, how waypoints are formatted, which travel modes and units exist,
and how the response differs between format=geojson and format=json.
Then make one real request between two points in Berlin and summarize it.What happened in our test:
- It correctly found that only
waypointsandmodeare required. Waypoints arelat,lonpairs separated by|, from 2 to 1000 points, and the specification lists 17 travel modes. - It explained that
units=metricreturns meters andimperialreturns miles. The defaultgeojsonformat returns a FeatureCollection with aMultiLineStringgeometry (one line per leg, coordinates[lon, lat]), whileformat=jsonreturnsresults[0]with geometry as arrays of{lon, lat}objects. - Swapping the coordinates returned HTTP 400 with a helpful message:
No suitable edges near location. Please check waypoint coordinate order (lat/lon). - Some things had to be inferred: the specification doesn't state the unit of
time(seconds), andimperialalso changes the distances of individual steps.
The agent's real request was a REST call (GET):
https://api.geoapify.com/v1/routing?waypoints=52.5163,13.3777|52.5208,13.4095&mode=drive&apiKey=YOUR_API_KEYSimplified response:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": { "type": "MultiLineString", "coordinates": ["..."] },
"properties": {
"mode": "drive",
"distance": 2428,
"distance_units": "meters",
"time": 216.851,
"waypoints": [
{ "location": [13.3777, 52.5163], "original_index": 0 },
{ "location": [13.4095, 52.5208], "original_index": 1 }
],
"legs": [
{
"steps": [
{ "distance": 17, "time": 3.167, "instruction": { "text": "Drive south on Pariser Platz." } },
"... 4 more steps"
]
}
]
}
}
]
}Note how the request uses lat,lon while the response returns [lon, lat]. The Routing API documentation covers all options.
Prompt 4: Research with MCP, Ship with OpenAPI
The last prompt combines both interfaces: the agent explores with Geoapify MCP tools, then writes code that does not need MCP at runtime:
Use the Geoapify MCP server to find cafés within about 500 m of Alexanderplatz,
Berlin, and report which categories and filters you used. Then write production
TypeScript with ./specs/places.json that reproduces the same result through the
Places REST API, without MCP at runtime.What happened in our test:
- MCP
tools/listonhttps://api.geoapify.com/v1/mcpshowed 11 tools. The MCP tool callgeocode_addressreturned Alexanderplatz at lat52.5219814, lon13.4136358, andlist_place_categoriesreturned 833 categories. - The MCP tool call
search_placeswithcategory: "catering.cafe"andradius_meters: 500found 29 cafés between 79 and 482 m away, starting with Cuccis (79 m) and Einstein Kaffee (129 m). - The generated REST request to the Places API returned the same 29 places in the same order.
The REST request the agent shipped (GET):
https://api.geoapify.com/v2/places?categories=catering.cafe&filter=circle:13.4136358,52.5219814,500&bias=proximity:13.4136358,52.5219814&limit=100&apiKey=YOUR_API_KEYThe key lines of the generated TypeScript show how it handled coordinate order and authentication:
const client = createClient<paths>({
baseUrl: "https://api.geoapify.com/v2",
headers: { "x-api-key": apiKey }, // keeps the key out of URLs and logs
});
// Geoapify spatial strings are lon,lat (not lat,lon); radius is in meters.
const filter = `circle:${center.lon},${center.lat},${radiusMeters}`;
const bias = `proximity:${center.lon},${center.lat}`;
const { data, error } = await client.GET("/places", {
params: { query: { categories, filter, bias, limit: pageSize, offset, lang } },
});We then tested the pitfalls live against the REST API:
- Swapped coordinates in
circle:(lat,lon) returned 0 results and no error. - A radius of
0.5(thinking in kilometers) returned 0 results, because the radius is in meters. - Without
bias=proximity, the results were the same places in a different order, with nodistancefield. The MCP tool adds proximity ordering for you. categories=cafereturned HTTP 400:Category "cafe" is not supported.- Omitting
limitreturned only 20 of the 29 cafés: the REST default is 20, with a maximum of 500.
See the Places API documentation for the category list and filter syntax.
What Does AI Handle Well, and What Should You Verify?
Across all four runs, the specification consistently prevented the classic mistakes: invented endpoints, wrong parameter names, and missing authentication. The remaining issues were about meaning rather than syntax: coordinate order, units, ranking, and defaults.
| AI handles well with a specification | Verify yourself |
|---|---|
Endpoints and required parameters (waypoints and mode for routing) | Coordinate order in strings: lon,lat in circle, rect, proximity, and GeoJSON; lat,lon in routing waypoints |
Enums turned into TypeScript types (lang, countrycodes, format) | Units: meters for radius and distance, seconds for time |
Authentication placement (x-api-key header or apiKey query) | Array and filter serialization against the documentation, plus API key exposure in browser code |
Error shapes from schema examples (statusCode, error, message) | Ranking and bias: a correct request can still return the wrong city first |
Boilerplate such as debounce, AbortController, and pagination | Defaults such as limit=20 in the Places API |
Explaining response formats (geojson vs. json) | Runtime validation and unknown keys: generated types are compile-time only, live responses can include undeclared fields such as ref, and extra query keys were not flagged in our test |
Before shipping AI-generated Geoapify code, run through a short checklist:
- Run one live request per operation and compare the result with what you expect on a map.
- Swap a coordinate pair on purpose and confirm the code would fail or return nothing.
- Check every numeric parameter for its unit.
- Compare the number of results with the
limityou need. - Confirm the key comes from the environment, a restricted browser key, or a proxy.
- Add runtime validation for the fields your UI depends on.
OpenAPI or Geoapify MCP: Which Should Your AI Use?
Both give an AI tool a machine-readable description of Geoapify, but at different times. An OpenAPI specification helps an AI write code that your application runs. The Geoapify MCP Server lets an AI agent call tools itself while it works.
| Criterion | OpenAPI specification | Geoapify MCP |
|---|---|---|
| Who uses it | AI coding tools, code generators, API clients | AI assistants and agents in MCP-compatible clients |
| When | Build time | Runtime |
| Determinism | Your code makes the same request every time | The model chooses tools and arguments |
| Runtime dependency | None beyond the REST API | An MCP client and model in the loop |
| API surface | The full documented REST API | A curated set of tools (11 in our test) |
| Best for | Shipping product features and pipelines | Research, exploration, ad hoc location questions |
The two meet in the focused MCP tool specifications listed in mcp/tools/index.json. They describe individual MCP tools, such as search_places or calculate_route, as OpenAPI operations that use the x-api-key header, so the same tooling can read them.
Prompt 4 shows the combined pattern: research with MCP, then ship deterministic code generated from the OpenAPI specification. For a broader comparison, read MCP vs. REST APIs: Which Should You Use for AI Applications?.
Build with Geoapify OpenAPI and MCP in Batch #2
The workflows in this article are exactly what the Open Geospatial Program Batch #2: "AI × Geospatial" is about. The theme is Build with Geoapify OpenAPI & MCP: use an AI coding tool with the specifications, connect an agent to MCP tools, or combine both.
- Applications: October 1 to October 20, 2026, 23:59 UTC.
- Categories: Build, Agent, Automate, and Experiment. Research-style projects are welcome.
- Rewards: selected completed projects can receive a EUR 300 sponsorship, plus bonus opportunities.
- How to apply: email a short project proposal, as described in How to Apply for Batch #2.
We also want to hear where the specifications helped or confused your AI tool. Missing serialization details, undeclared fields, or unclear units are useful reports: open an issue in the geoapify-openapi-specs repository.
Frequently Asked Questions
Are the Geoapify OpenAPI specifications free to use?
Yes. The geoapify-openapi-specs repository is licensed under the MIT License. Calling the APIs themselves requires a Geoapify API key; see Geoapify pricing for the free tier and plans.
Which OpenAPI version do the Geoapify specifications use?
They use OpenAPI 3.1.0. Use code generators and tools that support OpenAPI 3.1. The openApiVersion field in index.json shows the version of each file.
Should I give my AI agent all specifications or only one?
Only the ones the task needs. Specifications range from about 60 KB (Forward Geocoding) to about 250 KB (Batch), so loading all of them wastes context and exposes unrelated operations. Use index.json to pick the right file.
Can I generate clients for languages other than TypeScript?
Yes. OpenAPI Generator supports many languages and frameworks. Choose a generator that supports OpenAPI 3.1, and expect to add authentication handling and runtime validation.
How do I authenticate requests from a generated client?
Send the key in the x-api-key header for server-to-server code, or as the apiKey query parameter where a header is not practical. Read the key from an environment variable and create it in the Geoapify Projects dashboard.
When should I use MCP instead of an OpenAPI specification?
Use Geoapify MCP when an AI agent should call location tools at runtime, for example to answer ad hoc questions. Use OpenAPI when an AI coding tool should write deterministic code that your application runs. See MCP documentation.
Where can I report a problem in a specification?
Open an issue in the geoapify-openapi-specs GitHub repository. The JSON files are generated, so describe the problem rather than patching the generated file.
