> ## Documentation Index
> Fetch the complete documentation index at: https://docs.loci.ng/llms.txt
> Use this file to discover all available pages before exploring further.

# Introduction

> Base URL, the response envelope, the credential, and the four calls.

## Base URL

```
https://api.staging.loci.ng
```

Every call is under `/v1/frames`. The examples in this reference were
captured against a local development stack at `http://localhost:8088`; the
shapes are identical.

## The envelope

Every response is wrapped, so you write one parser and one error handler.

<CodeGroup>
  ```json Success theme={null}
  { "data": { } }
  ```

  ```json Failure theme={null}
  {
    "error": {
      "code": "invalid_point",
      "message": "lon and lat are required, in degrees"
    }
  }
  ```
</CodeGroup>

`code` is machine-readable and stable; branch on it. `message` is a human
sentence and may be reworded. Optional fields are omitted when empty rather
than sent as `null`: an absent `truncated`, an absent `charged`.

The one request body in this reference, on the question call, is decoded
strictly. An unknown field is `400 invalid_body` naming the field, and bodies
are capped at 1 MiB.

## The credential

An API key, minted in the app under **Developers → New key**, sent as a bearer
token:

```
Authorization: Bearer loci_sk_...
```

A key acts for one organisation, reads and nothing else, and expires. It is
read from the `Authorization` header only. An unknown, expired or revoked key
is `401 invalid_key`; no key at all is `401 unauthorized`; a valid key without
the capability a call needs is `403 insufficient_scope`, with the message
naming the capability. See [Authentication](/authentication) and
[Scopes](/scopes).

## The four calls

| Call | Capability | Answers |
| - | - | - |
| `GET /v1/frames/at?lat=&lon=` | `frame.read` | The frame containing a point: id, shape, bounding box. |
| `GET /v1/frames?bbox=` | `frame.read` | The frames touching a box, as a GeoJSON `FeatureCollection`. |
| `GET /v1/frames/data?ids=` | `frame.read`, plus `feature.read` for features | What every readable dataset records on each frame. |
| `POST /v1/frames/insight` | `ask.query` | A worded, checked answer about some frames. |

A frame id is the thread through all four: the first call produces one, the
second produces many, the last two take them.

## Rate limiting

Every call is metered per key, 60 a minute with a burst of 120, and per
organisation, 600 a minute. The question call carries a third budget of 20 a
minute. Over budget is `429 rate_limited` with a `Retry-After` in seconds, and
every metered response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`
and `X-RateLimit-Reset`. Details in [Limits](/limits).

## Caps

A box query returns at most 5,000 frames and sets `"truncated": true` when it
held more. The data call takes at most 100 frame ids and returns at most 200
features per dataset. A question covers at most 50 frames and 1,000
characters. Caps on a count refuse with `400`; caps on a result truncate and
say so.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.