Request context
Every request carries context that determines what it returns. Most of it comes from the key rather than from the request, which is deliberate: the fewer things a caller can assert about who it is, the fewer things can be forged.
Workspace comes from the key
You never send a workspace identifier. The key is bound to exactly one workspace, and that binding is fixed at creation.
There is no header to override it, and no parameter to switch it. To read a second workspace, create a key in that workspace. This is the difference from APIs that use an account header: an Asky key cannot be pointed at a workspace it was not issued for, whether by mistake or otherwise.
Brand comes from the path
Most data is brand-scoped, and the brand is a path segment.
GET /v1/brands/{brand_id}/visibility
GET /v1/brands/{brand_id}/prompts
GET /v1/brands/{brand_id}/citationsGet valid ids from GET /v1/brands, which returns only the brands your key may reach.
If a key is restricted to specific brands, any other brand returns 404 not_found, exactly as a brand in someone else’s workspace would. The API does not distinguish “exists but not yours” from “does not exist”, so it cannot be used to discover what else exists.
Request identifiers
Every response carries an x-request-id header, and the same value appears in the body as request_id.
x-request-id: req_01J8Z3K9QWIt identifies that exact request in our logs. Log it on your side, especially for failures. Quoting it in a support conversation is the difference between us finding the request in seconds and asking you a series of narrowing questions.
Idempotency
Version 1 is read-only, so every request is naturally idempotent: repeating one has no effect beyond returning the data again. You do not need an idempotency key, and there is no header for one.
If write endpoints are introduced in a future version, they will accept an Idempotency-Key header so a retried request cannot apply an action twice. Read endpoints will not change.
Request headers
| Header | Required | Notes |
|---|---|---|
Authorization | Yes | Bearer asky_sk_.... See API keys. |
Accept | No | application/json is assumed and is the only supported response type. |
Accept-Encoding | No | gzip is supported and worth enabling for large responses. |
User-Agent | Recommended | Identify your integration, for example acme-warehouse-sync/1.2. It helps us help you when something looks wrong. |
If-None-Match | No | For conditional requests. See caching. |
Response headers
| Header | Notes |
|---|---|
x-request-id | This request’s identifier |
RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset | Your current budget. See rate limits. |
Retry-After | On 429 and some 503 responses, seconds to wait |
ETag | Response fingerprint, for conditional requests |
Cache-Control | Always private. See caching. |
Cross-origin requests
The API does not send CORS headers, so it cannot be called from browser JavaScript. This is intentional. A key that works from a browser is a key that ends up in a front-end bundle where anyone can read it.
Call the API from your server, and pass whatever your front end needs through your own backend. If you want an in-browser experience over Asky data, use the Asky app or the MCP server.