
# API conventions

These conventions apply to the Search, Datasets, and Graph APIs in this guide.

## Base URL

The API is served from the DFO website's address, and every route sits under `/api/v1`.

```text
https://developmentfinance.org
```

The same routes also answer at `/api` without the version. Links that the API returns in a response use that form.

Use JSON request bodies and send `Content-Type: application/json` for requests with a body.

## Versioning

Datasets requests and responses use `schema_version: 1`. Search responses also carry `schema_version: 1`. The Graph API has its own response shapes, which `GET /api/v1/graph/schema` publishes.

`GET /api/v1/datasets/capabilities` reports live Datasets formats, input kinds, feature flags, limits, and a capability version. Treat capability values as deployment data.

When the graph does not declare a facet default, the Datasets API uses `dfo/facet-policy/most-observations` version `1` as its deployment fallback.

## Discover routes and contracts

| Route | Use |
| --- | --- |
| `GET /api/{search,datasets,graph}/healthz` | Check that an API is available and identify its build. |
| `GET /api/{search,datasets,graph}/capabilities` | Read deployment capabilities and limits. |
| `GET /api/v1/search/schema` | Read Search request and response schemas. |
| `GET /api/v1/search/catalogue` | Read available data and example questions. |
| `GET /api/v1/datasets/schema` | Read Datasets request and response schemas. |
| `GET /api/v1/graph/schema` | Read Graph request and response schemas. |
| `POST /api/v1/graph/nodes` | Read several graph nodes in one request. |
| `GET /codes/{code}` | Read the registry entry for a problem code. |
| `GET /openapi.json` | Read the OpenAPI document. |
| `GET /docs` | Open the interactive API documentation. |

## Trace headers

Application responses carry `x-dfo-trace-id`, except shared-cacheable responses. Include this identifier when reporting a problem. Search diagnostics and problem-details bodies carry the same trace identifier.

`x-dfo-commit` identifies the application build that answered the request. `Server-Timing` reports request timing where the response can carry it.

## Problem details

Application-level non-2xx responses use `application/problem+json` with RFC 9457 fields and DFO extensions. A response has at least these fields:

```json
{
  "type": "/codes/request_invalid",
  "title": "Bad Request",
  "status": 400,
  "detail": "Request body is invalid.",
  "code": "request_invalid",
  "trace_id": "...",
  "capability_version": "...",
  "retryable": false
}
```

`pointer` identifies the invalid location in a request body when available. `alternatives`, `issues`, `cause`, `constraint_id`, and ceiling details appear for the error types that define them. Branch on `code` and use `retryable` to decide whether to retry the same request.

Responses generated by infrastructure outside the application can use a different body.

## Status codes

| Status | Meaning |
| --- | --- |
| `200` | Request completed. Search can still report `candidates`, `no_data`, or `unsupported` in a part. |
| `202` | A Datasets operation is running. Poll the URL in `operation.poll`. |
| `400` or `422` | Request validation or a request that cannot be satisfied. Read the problem `code` and `pointer`. |
| `404` | The requested graph node, spec, snapshot, or operation is unknown. |
| `410` | A snapshot is revoked or its artifacts are no longer servable. |
| `500`, `502`, `503`, or `504` | The application could not complete the request. Check `retryable`. |

A Datasets `503` that reports a full instance carries `Retry-After` in seconds. Wait at least that long before you retry.

## Graph limits

`GET /api/v1/graph/capabilities` reports the current Graph request and identifier limits. Read it before generating large resolver or node batches.

## Next

[How to resolve a question](resolve-a-question.md) documents Search outcomes. [How to get a dataset](get-a-dataset.md) documents asynchronous operations and snapshot retention.
