# REST API

**Base URL:** `https://api.scrollstash.com` · **OpenAPI:** [`https://api.scrollstash.com/v1/openapi.json`](https://api.scrollstash.com/v1/openapi.json) · **Catalog:** [/docs/catalog.md](/docs/catalog.md)

## Authentication

Send an API key as a bearer token:

```bash
curl -s https://api.scrollstash.com/v1/me -H "Authorization: Bearer $SCROLLSTASH_API_KEY"
```

- Keys start with `ss_live_`, belong to one workspace and carry scopes. The plaintext is shown once, at creation.
- Self-service key creation is not open yet.
- Without a valid key the API answers `401`.

## Responses

Success is always `{ "data": …, "meta": { "request_id", "as_of", "cursor"?, "count"?, "lag_hours"? } }`.

Errors are always `{ "error": { "type", "message", "status", "request_id", "details"? } }`:

| type | status | meaning |
| --- | --- | --- |
| `bad_request` | 400 | input rejected; `details` has the issues |
| `unauthorized` | 401 | missing or invalid key |
| `payment_required` | 402 | plan quota used up (e.g. tracked-brand slots) |
| `forbidden` | 403 | key or plan lacks the scope |
| `not_found` | 404 | no such resource |
| `conflict` | 409 | already exists or changed |
| `rate_limited` | 429 | wait for `x-ratelimit-reset` |
| `internal` | 500 | retry; report the `request_id` |

## Workspaces

Paths with `{ws}` act on a workspace: an API key's own workspace id (see `GET /v1/me` → `data.workspace_id`).

## Pagination

List operations take `limit` (1–100, default 20) and `cursor`. Pass `meta.cursor` back until it is absent.

## Limits

Meta Ad Library data only (Facebook and Instagram ads), US ecommerce focus. No spend, reach, impressions or ROAS: how long an ad runs is observed survival, not performance. Every response carries `as_of`. Details: [/docs/limits.md](/docs/limits.md).
