# Unmet demand

`GET https://stylor.ai/api/v1/analytics/unmet-demand`

- Authentication: private API key, sent as `Authorization: Bearer sgpt-sk-…::…`
- Rate-limit resource: `api.v1.analytics.unmetDemand`
- Demo key: not accepted
- Web page: https://stylor.ai/guides/rest/v1/unmet-demand

What shoppers asked for that the catalogue could not answer, most asked
first: searches that found nothing, and things they named that no
product matched. Phrases only; nothing ties one to a shopper or a
session.

A search with `unfiltered` above zero still found nothing when its
filters were dropped, so the catalogue does not carry it, rather than
the filters being too narrow.

## Example request

```bash
curl -X GET "https://stylor.ai/api/v1/analytics/unmet-demand" \
  -H "Authorization: Bearer $STYLOR_API_KEY"
```

## Query parameters

- `days` (integer): How many days, ending today. [default `30`; one of `7`, `30`, `90`]
- `limit` (integer): Rows per list. [default `25`; minimum 1; maximum 100]

## Responses

### 200

The two lists.

Content type `application/json`:

- `timezone` (string, required): The timezone the days are counted in.
- `range` (integer, required): The number of days.
- `startDay` (string, required): The first day, `YYYY-MM-DD`.
- `endDay` (string, required): The last day, `YYYY-MM-DD`.
- `searches` (array<object>, required): Searches that found nothing, by query text.
  - `term` (string): The query
  - `count` (integer): How many times.
  - `unfiltered` (integer): How many of those still found nothing with every filter dropped.
  - `lastSeen` (string (date-time)): When it was last asked.
- `misses` (array<object>, required): Things shoppers named that no product matched, by term.
  - `term` (string): The term
  - `count` (integer): How many times.
  - `lastSeen` (string (date-time)): When it was last asked.

Example:

```json
{
  "timezone": "America/Denver",
  "range": 30,
  "startDay": "2026-09-05",
  "endDay": "2026-10-04",
  "searches": [
    {
      "term": "white cowboy boots",
      "count": 41,
      "unfiltered": 41,
      "lastSeen": "2026-10-04T15:02:44.000Z"
    }
  ],
  "misses": [
    {
      "term": "bolo tie",
      "count": 17,
      "lastSeen": "2026-10-03T21:40:10.000Z"
    }
  ]
}
```

## Errors

Every error is a JSON object with an `error` string. Match on the status code; the message is written for people.

| Status | Message | When |
| --- | --- | --- |
| 422 | days must be one of 7, 30, 90. | `days` is something else. |
| 422 | limit must be a whole number from 1 to 100. | `limit` is not a whole number, or outside 1 to 100. |

### Shared authentication, quota and rate-limit errors

| Status | Message | When |
| --- | --- | --- |
| 401 | API key missing. | The `Authorization` header is absent, is not `Bearer <key>`, or the key does not have three dash-separated segments. |
| 401 | Authentication failed: public key is invalid or not recognized. | A public-key endpoint was called with a key that does not exist. |
| 401 | Authentication failed: private key provided instead of a public key. | A public-key endpoint was called with an `sgpt-sk-…` key. |
| 401 | Authentication failed: token is not recognized. | A private-key endpoint was called with a key whose lookup segment does not exist. |
| 401 | Authentication failed: private key is invalid. | A private-key endpoint was called with a key whose secret does not match. |
| 401 | Authentication failed: public key provided instead of a private key. | A private-key endpoint was called with an `sgpt-pk-…` key. |
| 401 | Authentication failed: bearer token format is invalid. | A private key was sent without its `::` lookup segment. |
| 401 | Authentication failed: API key could not be read. Regenerate the key. | The key's embedded organization data could not be decrypted or parsed. |
| 403 | This key does not have the "<scope>" scope. | A private-key endpoint was called with a key whose scopes do not include the one the endpoint needs. Nothing is counted against your quota. See Authentication. |
| 403 | This key's scopes do not cover this endpoint. | A key with scopes called a private endpoint that no scope grants yet. Give the key every scope to reach it. |
| 403 | Demo mode is not available for private-key routes. Demos are only supported with public API keys. | The demo key was sent to a private-key endpoint. |
| 403 | Demo mode is not supported on this API route. Check the documentation for available demo endpoints. | The demo key was sent to a public-key endpoint that opts out of demo mode. |
| 403 | No active subscription for this organization. | The organization that owns the key has no active subscription. |
| 403 | No quota found for organization "<organizationId>". | The organization has no quota record for the current billing period. |
| 403 | No entitlement for "<resource>". | The plan's quota template has no entry for this endpoint. |
| 403 | Access denied to "<resource>". | The plan's quota template turns this endpoint off. |
| 429 | Monthly quota exceeded for "<resource>" (<quota>/<quota> used). | The billing-period quota for this endpoint is exhausted. `Retry-After` gives the seconds until the period resets. |
| 429 | RPM limit exceeded for "<resource>" (<used>/<limit> used). | More than the allowed requests per minute were sent. `Retry-After` is 60. |
| 500 | Internal server error. | An unexpected failure on the server. The message is always this string; details are logged, never returned. Quota consumed by the request is refunded, as it is for every 5xx response. |
