Build an OGC API Features service: troubleshooting guide
An OGC API Features service exposes geographic features through a standards-based Web API. The most reliable way to troubleshoot one is to work from the outside in: confirm the landing page and conformance declarations, inspect collections and OpenAPI metadata, test a small feature query, then isolate CRS, filtering, pagination, data-provider, and deployment problems. This guide uses pygeoapi as a concrete implementation example while keeping the diagnostic method applicable to other OGC API Features servers.
OGC API Features is a multi-part standard for discovering and querying spatial features on the Web. Part 1: Core defines the mandatory read-oriented capabilities, while additional parts extend the API with coordinate reference systems, filtering, and transactional operations. A service can therefore look healthy in a browser while still failing interoperability because its links, collection metadata, CRS declarations, pagination behavior, or response content do not match what clients expect. (ogcapi.ogc.org) This troubleshooting guide starts with a minimal request sequence and adds complexity only after each layer works. The examples use curl and pygeoapi because pygeoapi is a documented Python implementation of OGC API standards with providers for GeoJSON, CSV, OGR, PostgreSQL, Elasticsearch, and other backends. The implementation choice is not part of the OGC standard; it is simply a practical way to reproduce the checks locally. (docs.pygeoapi.io)
How EDK fits
For: GIS developers, geospatial API engineers, data publishers, platform operators, and teams migrating from older WFS-style services to modern OGC API building blocks.
An OGC API Features service is a focused interface for exposing the entities that describe the physical world: roads, buildings, parcels, hydrants, parks, waterways, service requests, or other geographic features. Its value is not the map display itself; it is the ability to retrieve individual features, inspect their attributes and geometries, constrain them spatially, and pass them into downstream computation. That makes OGC API Features relevant to the broader problem of finding and understanding places. A client can discover a collection of street features, request only those within a bounding box, retrieve selected properties, and use the results in a map, a spatial join, a routing workflow, or an operational analysis. The service remains one interoperable access layer rather than a complete world model: it does not automatically provide every dataset, geocoder, routing engine, change-detection system, or analytical operation. The connection to E2 is therefore at the interface and computation level: an OGC API Features endpoint can be one standards-based doorway into a larger searchable and computable representation of Earth.
- •Expose physical features such as roads, buildings, parks, hydrants, waterways, or public assets through a common feature-level interface, then use the returned geometries and attributes as inputs to geographic search, proximity calculations, regional summaries, or change analysis.
- •Combine multiple OGC API Features collections with other geographic sources so a system can answer questions such as which service requests fall near a road, which buildings are inside a planning area, or which public assets are within a specified distance of a waterway. The API supplies feature access; the surrounding world-model or computation system supplies the cross-dataset reasoning.
The EDK outcome
Before you start
A running OGC API Features endpoint or a local pygeoapi installation.
A dataset containing geographic features with stable identifiers and a documented source CRS.
Basic familiarity with HTTP methods, JSON, GeoJSON, coordinate reference systems, and bounding boxes.
curl or an equivalent HTTP client; jq is optional but useful for inspecting JSON.
Access to application logs, reverse-proxy logs, and provider configuration.
A known feature, coordinate, or bounding box that can be independently verified.
Learning objectives
Identify whether a failure occurs in discovery, collection metadata, feature retrieval, CRS handling, provider execution, or deployment.
Test the minimum OGC API Features request sequence before adding optional parameters.
Diagnose common coordinate-order, storage-CRS, bbox-crs, and output-CRS mistakes.
Compare declared conformance with the capabilities actually implemented by a backend.
Separate API interoperability problems from data-quality, freshness, licensing, and infrastructure problems.
Verification
The landing page returns the expected HTTP status and contains usable links to conformance, collections, and API definition resources.
The conformance response matches the capabilities the service is intended to provide.
The collections response contains stable collection IDs, titles, descriptions, extent information where available, and links that resolve through the public deployment path.
A one-feature request returns valid GeoJSON or another documented representation with the expected identifier and geometry.
A known bbox returns features in the expected area without coordinate-order or CRS displacement.
Pagination returns deterministic pages and usable next links when the service provides them.
The service rejects unsupported parameters clearly or documents their absence rather than silently returning misleading results.
The deployed endpoint preserves query parameters, content types, authentication behavior, CORS policy, and generated links.
Troubleshooting
Landing page returns 404: check the configured URL prefix, reverse-proxy path rewriting, trailing-slash behavior, and whether the application is mounted at the path used by generated links.
Collections endpoint is empty: inspect resource discovery configuration, file permissions, provider initialization errors, and whether resources are hidden by configuration.
Items endpoint returns 500: read the server traceback and provider logs; common causes include a missing data file, invalid database credentials, an absent optional Python dependency, an incorrect layer name, or a geometry parsing error.
Features appear in the wrong location: verify longitude/latitude ordering, source CRS, storage_crs, output crs, bbox-crs, and whether a transformation has been applied twice.
bbox returns no features: confirm the bbox order, coordinate units, antimeridian behavior if relevant, and the CRS used for the bbox. Test the same area in the default CRS before adding bbox-crs.
limit is ignored: check whether the request is reaching the expected endpoint, whether a gateway rewrites the query string, and whether the provider or implementation applies its own maximum page size.
resulttype=hits fails: counts are provider-dependent. Confirm that the provider supports resulttype=hits and that the service advertises the related capability.
Property selection or sorting fails: inspect the provider capability matrix and verify that the requested property exists with the expected case and type.
CORS works locally but fails in the browser: inspect Access-Control-Allow-Origin, allowed methods, allowed headers, credential handling, and OPTIONS preflight responses at the public gateway.
OpenAPI links are wrong: configure the public URL, host, scheme, and URL prefix at the application or proxy layer so generated links reflect the address clients actually use.
Responses are slow: measure provider execution separately from network time, add or verify spatial and attribute indexes where applicable, cap page sizes, limit returned properties, and avoid unbounded feature downloads.
Data is stale or incomplete: treat this as a pipeline and provenance issue rather than an API protocol issue. Verify source refresh jobs, timestamps, update semantics, filters applied during ingestion, and the collection’s declared extent.
Next steps
Move from the troubleshooting workflow to the production guide for architecture, deployment, authentication, refreshes, monitoring, and capacity planning.
Test the service with at least one real client such as a GIS application, a Python HTTP client, or a browser map, while retaining raw HTTP tests for regression coverage.
Add automated contract tests for landing-page links, conformance declarations, collection metadata, CRS behavior, bbox queries, pagination, and representative feature IDs.
Document the provenance, refresh cadence, licensing, source CRS, supported parameters, maximum page size, and known provider limitations for every collection.
If the API is one input to broader geographic computation, define how its feature IDs, timestamps, geometries, and source versions will be joined with roads, buildings, infrastructure, environmental features, or operational records.
What you need before debugging
You need a running service URL, access to its logs, one known collection identifier, and a small set of expected feature IDs or geometries. If the service is protected, obtain a test credential and confirm whether authentication is handled by the application, a reverse proxy, or an API gateway. Also record the intended OGC API Features parts: a Part 1-only service has different expectations from a service that advertises Part 2 CRS support or Part 3 filtering.
For a pygeoapi-based deployment, keep the runtime configuration and generated OpenAPI document available. pygeoapi uses configuration files for the server and resources, and its documentation provides provider-specific capability matrices; support for parameters such as bbox, resulttype, sortby, datetime, CRS, and transactions varies by provider. Do not assume that a parameter shown in the standard is implemented by every backend. (docs.pygeoapi.io)
- •A service base URL, such as https://api.example.com/
- •A collection ID, such as roads or hydrants
- •curl or another HTTP client
- •A JSON formatter such as jq, if available
- •Server logs and the data-provider configuration
- •A known-good feature identifier and expected coordinate reference system
- •The target OGC API Features conformance classes and supported response formats
Step 1: reduce the service to the discovery sequence
Do not begin with a complicated items query. First verify that the server presents the resources that allow a client to discover its capabilities. OGC API Features uses a landing page, a conformance declaration, collections metadata, and feature-item resources as the basic path through the API. The Core standard is deliberately read-oriented and supports discovery and query operations through HTTP GET. (docs.ogc.org)
Run the following requests in order, replacing the base URL with the actual deployment:
- •curl -i https://api.example.com/
- •curl -i https://api.example.com/conformance
- •curl -i https://api.example.com/collections
- •curl -i https://api.example.com/api
- •curl -i https://api.example.com/collections/roads
- •curl -i 'https://api.example.com/collections/roads/items?limit=1'
Step 2: validate the smallest feature response
A successful HTTP status is not enough. Inspect the response body and headers. For a GeoJSON response, check that the top-level object is a FeatureCollection or Feature, each feature has a stable identifier where the dataset promises one, geometries are valid GeoJSON geometries, and coordinates follow the advertised CRS and axis-order rules. Part 1 Core uses WGS 84 with longitude/latitude ordering for geometry responses by default; Part 2 extends the service to support additional CRS identifiers. (docs.ogc.org)
Use a deliberately small query first:
Step 3: make CRS behavior explicit
CRS errors are among the most damaging because the response may look plausible while placing features in the wrong location. Distinguish the storage CRS, the CRS used for the bbox, and the CRS used for returned geometries. Confirm whether the service supports the requested CRS and whether the response includes a Content-Crs header or equivalent metadata.
For pygeoapi, provider configuration uses storage_crs for the source data and crs as the list of supported output coordinate reference systems. The documentation states that the default is OGC CRS84, with WGS 84 coordinates in longitude/latitude order, and shows explicit crs and bbox-crs query parameters for alternate references. (docs.pygeoapi.io)
- •curl -i 'https://api.example.com/collections/roads/items?limit=1'
- •curl -i 'https://api.example.com/collections/roads/items?crs=http%3A%2F%2Fwww.opengis.net%2Fdef%2Fcrs%2FEPSG%2F0%2F3857&limit=1'
- •curl -i 'https://api.example.com/collections/roads/items?bbox=-8238000,4970000,-8237000,4971000&bbox-crs=http%3A%2F%2Fwww.opengis.net%2Fdef%2Fcrs%2FEPSG%2F0%2F3857&limit=1'
Step 4: verify provider configuration and query support
If discovery works but item queries fail, inspect the provider rather than the HTTP router. A collection backed by a static GeoJSON file has different capabilities from one backed by PostgreSQL, OGR, Elasticsearch, or a remote service. pygeoapi documents these differences explicitly: for example, providers vary in support for property filtering, counts, datetime, sorting, CQL, transactions, and CRS handling. (docs.pygeoapi.io)
A minimal pygeoapi resource configuration can look like this for a GeoJSON-backed collection. Adapt the exact server and metadata structure to the installed pygeoapi version and validate it with the project’s configuration tools before deployment:
- •resources:
- • roads:
- • type: collection
- • title: Roads
- • description: Road centerlines published as geographic features
- • providers:
- • - type: feature
- • name: GeoJSON
- • data: /data/roads.geojson
- • id_field: id
- • crs:
- • - http://www.opengis.net/def/crs/OGC/1.3/CRS84
- • storage_crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84
Step 5: test pagination, filtering, and response formats
Large collections should not be treated as one unbounded response. Test the limit parameter, follow the next link if one is provided, and confirm that page boundaries do not duplicate or skip features. Then test one concern at a time: bbox, property selection, resulttype=hits if supported, skipGeometry, sorting, temporal constraints, or filtering extensions.
Do not label a service non-compliant merely because it does not implement every optional or draft capability. Instead, compare the conformance declaration with the behavior it advertises. OGC API Features is a multi-part standard, and Part 3 filtering and Part 4 transactional operations are separate from the Core requirements. (ogcapi.ogc.org)
- •curl -s 'https://api.example.com/collections/roads/items?limit=10' | jq '.features | length'
- •curl -s 'https://api.example.com/collections/roads/items?limit=10&skipGeometry=true' | jq '.features[0].geometry'
- •curl -s 'https://api.example.com/collections/roads/items?bbox=-73.99,40.70,-73.98,40.71&limit=10' | jq '.features[].id'
- •curl -i 'https://api.example.com/collections/roads/items?resulttype=hits'
Step 6: validate deployment separately from data correctness
Once direct local requests work, test the deployed service through its complete network path. Reverse proxies commonly cause incorrect base URLs, broken relative links, lost query strings, unexpected redirects, CORS failures, compressed-response problems, or authentication headers that never reach the application. Compare a direct application request with the public URL and inspect the generated links in the landing page, collections response, and item response.
For production, use HTTPS, apply authentication and authorization appropriate to the data, configure request and response limits, log request IDs and latency, and monitor provider errors separately from HTTP errors. The OGC Common guidance encourages HTTPS and does not prescribe one authentication method, so security remains an implementation and deployment responsibility. (docs.ogc.org)
- •curl -i -L https://public.example.com/collections
- •curl -i -H 'Accept: application/geo+json' 'https://public.example.com/collections/roads/items?limit=1'
- •curl -i -H 'Origin: https://app.example.com' 'https://public.example.com/collections/roads/items?limit=1'
- •Compare direct-service and public-service responses for status, links, content type, CRS headers, and pagination.
Workflow
1. Capture the intended service contract
Write down the service base URL, expected collection IDs, advertised OGC API Features parts, supported media types, authentication method, source CRS, and one known feature. This prevents a client assumption from being mistaken for a server defect.
BASE='https://api.example.com'
COLLECTION='roads'
curl -i "$BASE/"
curl -i "$BASE/conformance"
curl -i "$BASE/collections"
curl -i "$BASE/collections/$COLLECTION"2. Test one feature before testing the whole dataset
Request one item or a very small page. Inspect the HTTP status, Content-Type, feature ID, geometry type, coordinates, properties, links, and any CRS-related headers. If this fails, do not proceed to filtering or application integration.
curl -i "$BASE/collections/$COLLECTION/items?limit=1"
curl -i "$BASE/collections/$COLLECTION/items/example-id"3. Test spatial selection with a verified bbox
Use a small bounding box whose coordinate order and CRS are known. Begin with the default CRS, then use bbox-crs only when the service advertises support for the requested CRS.
curl -s "$BASE/collections/$COLLECTION/items?bbox=-73.99,40.70,-73.98,40.71&limit=10" | jq .4. Test optional capabilities independently
Add one parameter per request: limit, skipGeometry, resulttype, property selection, sortby, datetime, filter, or crs. Record which parameters work for the selected collection and provider. Do not infer that support for bbox implies support for all other query operations.
curl -i "$BASE/collections/$COLLECTION/items?limit=10&skipGeometry=true"
curl -i "$BASE/collections/$COLLECTION/items?resulttype=hits"
curl -i "$BASE/collections/$COLLECTION/items?crs=http%3A%2F%2Fwww.opengis.net%2Fdef%2Fcrs%2FEPSG%2F0%2F3857&limit=1"5. Compare direct and public deployment paths
If local requests work but the public endpoint fails, compare redirects, generated links, query strings, CORS headers, authentication headers, compression, timeout behavior, and response content through the reverse proxy or gateway.
curl -i -L "$BASE/collections"
curl -i -H 'Origin: https://app.example.com' "$BASE/collections/$COLLECTION/items?limit=1"6. Run formal or implementation-specific validation
After smoke tests pass, compare the conformance declarations with the relevant OGC requirements and use the official compliance resources for the applicable parts. For pygeoapi, also validate its runtime configuration and generated OpenAPI document before promoting the service.
curl -s "$BASE/conformance" | jq .
curl -s "$BASE/api" | jq .Questions teams ask
Does an OGC API Features service replace WFS?
Not automatically. OGC API Features provides a newer Web API building-block approach for feature discovery and access, while WFS is a separate OGC service standard. A migration may require adapting clients, links, query parameters, response handling, authentication, and supported operations. Test the actual capabilities of the target service rather than assuming feature parity.
Why does my map show features in the wrong place even though the GeoJSON is valid?
The most common causes are confusing latitude/longitude with longitude/latitude, interpreting a projected coordinate pair as WGS 84, using the wrong bbox-crs, or applying a CRS transformation twice. Confirm the service’s default CRS, inspect the Content-Crs response header, check the collection metadata, and compare one known coordinate with an independent GIS viewer. OGC API Features Core uses WGS 84 longitude/latitude ordering by default, while Part 2 supports additional CRS identifiers. (docs.ogc.org)
Why does bbox work but a property filter or sortby request fail?
Those capabilities may not be supported by the selected provider or may require a separate conformance class. pygeoapi’s provider matrix shows that support differs across GeoJSON, CSV, OGR, PostgreSQL, Elasticsearch, and other providers. Check the service conformance response, provider documentation, and server logs before changing the client request. (docs.pygeoapi.io)
Should I expose the source database directly through OGC API Features?
Usually not without an explicit data-access boundary. A service layer should define collection metadata, stable identifiers, supported query parameters, access control, rate limits, response limits, logging, and data-refresh behavior. Direct database exposure can also leak fields or create query patterns that are difficult to control.
How do I test whether the service is actually conformant?
Begin by comparing the service behavior with the conformance classes it declares, then use the OGC compliance resources and validator for the relevant OGC API Features parts. A successful response from a few curl commands is useful smoke testing, but it is not the same as formal compliance testing. (ogcapi.ogc.org)
Keep exploring
build an ogc api features service production guide
The production guide is the natural next step after troubleshooting: architecture, deployment, refresh strategy, security, observability, and provider selection.
consume wfs in python
A useful adjacent workflow for teams that still need to read legacy WFS services while building or migrating to modern feature APIs.
analyze spatial data with geopandas
After retrieving features from an OGC API, GeoPandas provides a practical path for CRS-aware filtering, spatial joins, measurements, and analysis.
Sources
OGC API - Features overview
Official overview of the multi-part standard, Core, CRS by Reference, Filtering, draft transactional work, implementers, and compliance testing.
Open sourceOGC API - Features - Part 1: Core
Primary specification for discovery, query operations, feature-level access, default CRS behavior, and the read-oriented Core requirements.
Open sourceOGC API - Features - Part 2: Coordinate Reference Systems by Reference
Primary specification for using CRS identifiers beyond the defaults defined by Part 1.
Open sourcepygeoapi documentation: Publishing vector data to OGC API - Features
Official implementation documentation covering providers, query-parameter support, storage_crs, supported CRS values, and example requests.
Open sourcepygeoapi documentation: How pygeoapi works
Official description of pygeoapi as a Python-based HTTP server implementation and its configuration model.
Open sourceOGC API - Common - Part 1: Core
Official common guidance for OGC Web API resource patterns, HTTPS, API definitions, links, and security boundaries.
Open source