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

# Introduction

> Base URL, authentication, requests, responses, pagination, freshness, status codes and idempotency

The Alura API is a JSON API over HTTPS. This page covers what every endpoint shares.

## Base URL

```text theme={null}
https://api.alura.io/v1
```

The OpenAPI 3.1 document is at `https://api.alura.io/v1/openapi.json`.

## Authentication

Send an API key as a bearer token:

```bash theme={null}
curl https://api.alura.io/v1/me \
  -H "Authorization: Bearer $ALURA_API_KEY"
```

Workspace owners and admins create keys in [platform.alura.io](https://platform.alura.io/settings) under **Settings**, then **API keys**. A key belongs to one workspace: it spends that workspace's API credits and sees its data. `GET /v1/me` is free and returns the workspace, plan and scopes the key carries, so it's a good first call.

Call the API from a server or a script. A key sent from a browser is refused with `403 browser_refused`. AI assistants connect over MCP with OAuth instead of a key.

> [accordion] Scopes
>
> | Scope | Allows |
> | - | - |
> | `research:read` | Products, shops, keywords and categories |
> | `saved:read` | Reading saved folders, lists and items |
> | `saved:write` | Creating, changing and deleting saved research |
> | `usage:read` | `GET /v1/usage` and the usage history |
> | `workspace:read` | Your connected shops: shop details and settings, stats, listings, reviews, changes and syncs |
> | `orders:read` | Your shops' orders, which hold buyer data, and the buyer and order on each review |
> | `customers:read` | Your shops' customers, which hold buyer data |
> | `listings:write` | Creating, changing, publishing and deleting your Etsy listings, and reverting a change |
> | `shop:write` | Changing your shop's details and settings: sections, shipping profiles, return policies, processing profiles and holiday settings |
> | `orders:write` | Adding shipments to your orders |
> | `webhooks:write` | Creating and managing [webhook endpoints](https://docs.alura.io/guides/webhooks-and-events.md) |
> | `events:read` | Reading [events](https://docs.alura.io/guides/webhooks-and-events.md) |
>
> A new key gets every scope unless you name some. A call without the scope it needs answers `403 insufficient_scope`.

[Authentication](https://docs.alura.io/authentication.md) covers key rotation, MCP connections and workspaces in full.

## Requests

* **Query parameters** are snake\_case, such as `price_max` and `est_monthly_sales_min`. Lists are comma-separated: `state=active,draft`.
* **Bodies** are JSON with `Content-Type: application/json`.
* **IDs** are always strings, Etsy's included. Send `"4524717183"`, not `4524717183`.
* **Dates** are ISO 8601 in UTC, such as `2026-10-15T12:00:00.000Z`. Months are `YYYY-MM`.
* **Unknown parameters** are refused with `400 unknown_parameter`, so a typo never passes silently.

### Choosing fields

`fields` takes comma-separated dotted paths and returns only those. `id`, `object` and `as_of` always come back. An unknown path answers `400 invalid_request`, with the valid paths in `valid_fields`.

```text theme={null}
GET /v1/research/products?q=mug&fields=title,price.usd,sales.est_monthly_avg
```

### Expanding related objects

Some endpoints take `expand` to add related objects to the same response, for free, such as `expand=shop` on [Get product](https://docs.alura.io/api-reference/research/products/get.md). Each endpoint's page lists its values. An expansion never fetches from Etsy: when Alura has no data for it, the field is `null`.

## Responses

Every object has an `object` field naming its type.

* **Unknown is `null`, never `0`.** A `0` is a real zero.
* **Estimates** are fields that start with `est_`. Each has a [methodology page](https://docs.alura.io/methodology.md).
* **`Request-Id`** is a header on every response, errors included. Quote it when you contact support.
* **Cost headers.** `Alura-Credits-Cost` and `Alura-Credits-Remaining` are on every authenticated response. See [Credits and limits](https://docs.alura.io/api-reference/credits-and-limits.md).
* **`/v1` only grows.** Accept fields and enum values you don't know yet. See the [changelog](https://docs.alura.io/changelog.md).

## Pagination

Lists have the same shape everywhere. Pass `next_cursor` back as `cursor` to get the next page:

```json theme={null}
{
  "object": "list",
  "data": [{ "object": "research.product", "id": "4524717183", "title": "Personalized Name Mug, Custom Coffee Mug" }],
  "has_more": true,
  "next_cursor": "eyJ2IjoxLCJrIjoi...",
  "total": { "value": 10000, "relation": "gte" }
}
```

| Field or parameter | Meaning |
| - | - |
| `has_more` | `true` when another page follows. |
| `next_cursor` | The cursor for the next page. `null` on the last page. |
| `total` | How many items match. Counting stops at 10,000, and `relation` is then `gte`: at least that many. |
| `limit` | The page size: 25 by default, at most 100. A search costs the same per page whatever `limit` is. |
| `cursor` | The previous page's `next_cursor`. It works only with the same endpoint, filters, sort, key and workspace; anything else answers `400 invalid_cursor`. |

> [accordion] Paging limits
>
> * Paging stops after 10,000 results with `400 cursor_window_exceeded`. Narrow the query to reach further.
> * Ties are broken by ID, so a page boundary never splits two equal items. Values can change between pages, so an item can still move.
> * A few sub-lists use fixed pages of 25, and `limit` can only make them smaller: a shop's products, a shop's reviews, a product's reviews and related keywords.
> * Through MCP, pages default to 20 items and hold at most 50.

[Find products](https://docs.alura.io/guides/find-products.md) has a paging loop in curl, TypeScript and Python.

## Freshness

Alura serves Etsy data from its own copy. Every product, shop and keyword has `as_of`: when its data was last updated from Etsy, or `null` when Alura doesn't know. With `RESEARCH_READS_V2` on, search-backed product and shop summaries return `as_of: null` until P9c projects source-normalized clocks. Updating a search copy does not date the underlying data. With the switch off, summaries keep the search copy's update time, which can differ from the detail's.

> [!NOTE]
>
> Stale products aren't refreshed for now: they come back with `refresh: null`. Stale shops are refreshed. The [status page](https://docs.alura.io/status.md) says when this changes.

> [accordion] Stale data and refresh
>
> A product or shop whose `as_of` is more than 14 days old is stale. [Get product](https://docs.alura.io/api-reference/research/products/get.md) and [Get shop](https://docs.alura.io/api-reference/research/shops/get.md) return the data Alura has right away, charged as usual. Get shop also starts a refresh; Get product doesn't for now. The `refresh` object says how it's going:
>
> ```json theme={null}
> {
>   "refresh": {
>     "status": "queued",
>     "progress": null,
>     "started_at": null,
>     "estimated_ready_at": "2026-10-15T12:04:00Z",
>     "error": null
>   }
> }
> ```
>
> `refresh` is `null` when nothing is running. Ask again after `estimated_ready_at`: a shop takes a few minutes. Search, expansions, reviews, tags and saved items never start a refresh.

> [accordion] Items Alura has never seen
>
> Opening a product or shop Alura has never seen, or looking up a keyword it doesn't know, asks Etsy for it. That's a fetch, with [its own daily caps](https://docs.alura.io/api-reference/credits-and-limits.md#fetch-caps).
>
> | Answer | Meaning | What to do |
> | - | - | - |
> | `200 OK` | A product was fetched within the call. Charged as usual. | Use it |
> | `202 Accepted` | The fetch started. Free. `Retry-After` is the expected wait (5 seconds for a product or keyword and 60 for a shop when Alura can't estimate it). | Poll `Location` after `Retry-After` |
> | `404 not_found_on_etsy` | Etsy says the item doesn't exist. Alura remembers that for 24 hours. | Check the ID or name |
> | `404 shop_opted_out` | The product's shop asked Alura not to track it. Alura remembers that for 24 hours. | Drop it |
> | `429 fetch_limit_reached` | A fetch cap or the per-minute fetch rate. | Wait for `Retry-After` |
> | `404 not_collected` | Alura has paused fetching this kind of item. | Search for items Alura already has |
> | `503 fetch_unavailable` | A temporary queue refusal or a recently failed fetch. | Wait for `Retry-After` |
>
> Bulk calls never fail on one unknown ID. They sort every ID into `data` (known or just fetched, and the only ones charged), `pending` (a fetch started), `unfetched` (not fetched, each with a `code` and `retry_after`; `not_fetchable` means asking again won't help) and `not_found`. See [Bulk lookups](https://docs.alura.io/guides/bulk-lookups.md).
>
> Bulk calls fetch a few unknown items during the call: up to 5 products with [Get products by ID](https://docs.alura.io/api-reference/research/products/getMany.md), and up to 10 keywords with a keyword lookup, in about 10 seconds. Those that are ready come back in `data`; the rest are in `pending` or `unfetched`, free, with a short `retry_after`. Keywords are never `not_found`.

## Status codes

| Code | Meaning |
| - | - |
| `200` | Success. |
| `202` | Accepted: Alura started work, such as fetching a new item or writing to Etsy. |
| `400` | Something in the request is invalid: a parameter, a cursor or the body. |
| `401` | No API key, or the key is invalid or revoked. |
| `402` | The workspace has used this month's API credits. |
| `403` | The key lacks a scope, a plan feature or a role, or the request came from a browser. |
| `404` | No such item, or Etsy doesn't have it. |
| `409` | The write conflicts with the current state, or with an `Idempotency-Key` in use. |
| `422` | The request is valid but can't be done, such as saving an item Alura doesn't have. |
| `429` | A rate limit, a fetch limit or the daily API credit ceiling. Wait for `Retry-After`. |
| `500`, `502`, `503`, `504` | Alura or Etsy had a problem. Safe to retry with backoff. |

Errors are `application/problem+json` with a stable `code`. See [Errors](https://docs.alura.io/api-reference/errors.md).

## Idempotency

Saved research writes and writes to your Etsy shop take an `Idempotency-Key` header. Send a fresh UUID for each change, and reuse it only to retry that same change.

```http theme={null}
Idempotency-Key: 5f0c8e2a-4b1d-4c3e-9a7f-2b5d6e8f0a1c
```

| You send | You get |
| - | - |
| The same key and request within 24 hours | The first answer again, with `Idempotent-Replayed: true`. Nothing changes twice. |
| The same key with a different request | `409 conflict` |
| The same key while the first request still runs | `409 idempotency_processing`. Retry after a moment. |
| The same key after a failed request | The request runs again: a failure leaves no record. |

> [accordion] Writes to your Etsy shop
>
> Shop writes keep keys for 24 hours too, with two differences:
>
> * A refused write answers its refusal again. Only a write that failed with nothing written (`Retry-After` set) is tried again under its key.
> * While the first request still runs, the same key answers its `202` change once the write was accepted (`409 idempotency_processing` before that).
>
> A key is required for relative changes such as `adjust_price`, to add a listing file, image or video, and to change an image's alt text: each uploads to Etsy, so a blind retry would upload again. A `dry_run=true` needs none.

## Next steps

> [card] [Quickstart](https://docs.alura.io/quickstart.md)
>
> Make your first call in two minutes.

> [card] [Credits and limits](https://docs.alura.io/api-reference/credits-and-limits.md)
>
> What each call costs and how fast you can call.

> [card] [Search products](https://docs.alura.io/api-reference/research/products/search.md)
>
> The endpoint most scripts call first.


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