# gist developer docs

> Trends data API for developers and AI agents. Discover current trends and understand the context behind them, based on social conversations, aggregated across social platforms.

HTML version: https://gist.the-alt.co/docs

- **Keywords** (`POST /v1/keywords/{topic|all}`) discovers trends gaining attention or prominent now, with related keywords, metrics, supporting context and source posts. Its structured output helps tools decide what to surface, create, or explore.
- **Answers** (`POST /v1/search/{topic|all}`) explains what people are saying about a question, with supporting posts. Its output is prose for research or an agent's context.
- **Topics** (`GET /v1/topics`) lists the topics you can use in the path.
- **MCP server** (`https://gist-api.the-alt.co/mcp`) gives AI assistants Keywords and Answers as tools. See [Use gist in AI assistants (MCP)](#use-gist-in-ai-assistants-mcp).

## How to use gist

- **Topic** — area of gist coverage to search, such as `travel`.
- **Query** — the subject or market segment to explore within that area, such as `solo backpacking travellers`.
- **Trend** — patterns and themes gaining attention in social conversations related to your query.
- **Keywords** — related terms people use around a trend. The numerical metrics belong to these terms.

Start with Keywords to discover trends: `emerging` finds what is gaining attention, while `prevalent` finds what is prominent now. Use Answers when you need more context or want to ask your own question. Keywords and Answers work independently; an Answers request does not require a preceding Keywords request.

## Choosing an endpoint

| You need | Use | Key options |
| --- | --- | --- |
| A list of trends to rank, filter, display, or feed into another tool | Keywords, `format: "json"` | `mode`, `geo` |
| What is newly gaining attention | Keywords, `mode: "emerging"` (default) | `geo` |
| What is already prominent | Keywords, `mode: "prevalent"` | `geo` |
| A short prose overview of the trends | Keywords, `format: "text"` (result in `read`, `signals` is empty) | `mode`, `geo` |
| An answer to your own question, with cited source posts | Answers | `geo`, `weeks`, `content` |
| How posts present a subject (formats, hooks, angles) | Answers, `content: true` (result in `contentRead`) | `geo`, `weeks` |

Keywords has no `weeks` or `content`, and Answers has no `format` or `mode`. Unknown body fields are ignored, not rejected, so sending them changes nothing.

Choosing a topic: call `GET /v1/topics` and use a `topicId` with `searchable: true` in the path. Use `all` when the subject spans several areas or you are unsure which topic applies; gist picks the most relevant topics your key can access for the query. The response's `topics` array shows which topics were searched.

## Interpreting results

- **`noSignal: true` is a successful response, not an error.** It means there was not enough evidence to return supported results. Keywords returns an empty `signals` array; Answers explains the gap in `notes`. It still costs 1 unit unless cached. Do not retry it as a failure; try a broader query, a different topic, `all`, or no `geo`.
- **An empty `signals` array alone does not mean no signal.** Keywords also returns empty `signals` for `format: "text"`. Check `noSignal`.
- **`growth` is a relative score from -100 to +100.** Positive means rising, negative means declining, 0 is roughly flat. It is not a literal percentage change. `growth: -10` means the keyword was measured and is declining slightly. `growth: null` means there was not enough measurement data; it does not mean zero, flat, or declining. Report null as unknown.
- **`shareOfVoice` is 0–100.** `null` means not enough measurement data, not zero.
- A trend's related keywords do not all need positive growth, even in emerging mode.
- **`knowledgeCutoff`** is the date and time through which the information behind the results is up to date. Keep it with the results.
- **`notes`** (Answers) can describe evidence gaps or limits even when an answer is returned. Check it.

## Quickstart

The base URL is `https://gist-api.the-alt.co`.

Create an API key in the dashboard (https://gist.the-alt.co/dashboard/keys). Send it as a bearer token in the `Authorization` header. Keep your key in a server-side environment variable; do not include it in browser code.

```bash
export GIST_KEY="your_api_key"
```

```bash
curl -X POST https://gist-api.the-alt.co/v1/keywords/travel \
  -H "Authorization: Bearer $GIST_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query":"solo backpacking travellers"}'
```

## Use gist in AI assistants (MCP)

gist is also a remote MCP server, so AI assistants such as Claude, ChatGPT, Cursor and VS Code can use Keywords and Answers directly, without writing code. Add this server URL to your assistant:

```text
https://gist-api.the-alt.co/mcp
```

The first time your assistant uses gist, it opens a gist sign-in page. Enter your email, open the sign-in link we send you, then review and allow access. The assistant then runs queries as your account, with the same plan and units as the API: each uncached call costs 1 unit. It stays connected as long as you use it at least once every 90 days.

### Tools

| Tool | Same as | Arguments | Description |
| --- | --- | --- | --- |
| `keywords` | [Keywords](#keywords) | query, topic (default "all"), mode, geo, format | Discovers trends gaining attention or prominent now, with signal scores, strength bands, related keywords with metrics and intent classifications, supporting context and source posts. |
| `answers` | [Answers](#answers) | query, topic (default "all"), geo, weeks, content | Explains what people are saying about a question, with supporting posts. |

Both tools return the same results as their endpoints, including `noSignal`, `knowledgeCutoff` and `usage`, so the rest of this reference applies to them too.

### Add gist to your assistant

- **Claude** (web and desktop): go to Customize → Connectors → Add → Add custom connector, and paste the server URL. On Team and Enterprise plans, an owner adds it under Organization settings → Connectors. See [Claude's guide](https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp).
- **ChatGPT**: turn on developer mode, then add a custom MCP connector with the server URL and OAuth authentication. See [OpenAI's guide](https://help.openai.com/en/articles/12584461-developer-mode-and-mcp-apps-in-chatgpt) for where these settings are.
- **Claude Code, Cursor and VS Code**: use the configuration below.

Claude Code:

```bash
claude mcp add --transport http gist https://gist-api.the-alt.co/mcp
# then run /mcp in Claude Code and choose gist to sign in
```

Cursor (`.cursor/mcp.json`):

```json
{
  "mcpServers": {
    "gist": {
      "url": "https://gist-api.the-alt.co/mcp"
    }
  }
}
```

VS Code (`.vscode/mcp.json`):

```json
{
  "servers": {
    "gist": {
      "type": "http",
      "url": "https://gist-api.the-alt.co/mcp"
    }
  }
}
```

Clients that can't sign you in, and automated agents, can send an API key in the `Authorization` header instead. Keep the key out of config files you share or commit.

```json
{
  "mcpServers": {
    "gist": {
      "url": "https://gist-api.the-alt.co/mcp",
      "headers": {
        "Authorization": "Bearer your_api_key"
      }
    }
  }
}
```

## Keywords

`POST /v1/keywords/{topic|all}`

Keywords discovers trends gaining attention or prominent now, with related keywords, metrics, supporting context and source posts. Each trend in `signals` has a name (`label`), an explanation (`context`), related `keywords`, keyword-level metrics in `keywordStats`, and supporting `posts`.

`mode: "emerging"` finds trends gaining attention in the latest week of available data; it is the default. `mode: "prevalent"` finds trends prominent in that week.

### Understand the metrics

**Share of Voice** (`shareOfVoice`) estimates how much of the relevant social conversation is associated with a keyword in the latest week of available data. Higher values mean the keyword is more prominent in the conversation around your query.

**Growth** (`growth`) indicates whether a keyword is gaining or losing traction compared with recent weeks. Positive values mean rising, negative values mean declining, and 0 means roughly flat. This is a relative score, not a literal percentage change.

A keyword may not have enough measurement data, resulting in `null`. This does not mean zero.

### Path parameters

| Parameter | Type | Notes | Description |
| --- | --- | --- | --- |
| `topic` | `string` | required | A coverage area you can access, such as "travel" or "recipes". Use "all" to have gist pick the most relevant topics your key can access for the query. |

### Body parameters

| Parameter | Type | Notes | Description |
| --- | --- | --- | --- |
| `query` | `string` | required | What to explore within the topic, such as "solo backpacking travellers" (max 400 characters). |
| `geo` | `string` | optional | Filter by where authors or creators are based (max 120 characters). For example, "UAE" restricts results to UAE-based creators. More specific locations, such as "Dubai", are supported on a best-effort basis using creators’ stated locations. To explore a place as a subject, include it in query. |
| `format` | `"json" \| "text"` | default json | "json" returns structured trends in signals. "text" returns prose in read, with signals empty. The response is JSON in both cases. Either format costs 1 unit when uncached, or 0 when cached. |
| `mode` | `"emerging" \| "prevalent"` | default emerging | "emerging" finds trends gaining attention in the latest week of available data. "prevalent" finds trends prominent in the latest week of available data. |

### Response fields

| Field | Type | Notes | Description |
| --- | --- | --- | --- |
| `topics` | `string[]` |  | The topics searched. |
| `query` | `string` |  | Your query, echoed back. |
| `format` | `string` |  | The format used — "json" or "text". |
| `mode` | `string` |  | The mode used — "emerging" or "prevalent". |
| `knowledgeCutoff` | `string` | ISO 8601 | The date and time through which the information behind the results is up to date. |
| `noSignal` | `boolean` |  | True when there is not enough evidence to identify trends in the selected mode. This is a successful response, not an error. |
| `signals` | `object[]` |  | Trends related to your query. Empty for format "text" or when noSignal is true. |
| `signals[].label` | `string` |  | The name of the trend, such as "Alpine and Mountain Trekking". |
| `signals[].score` | `integer \| null` | 0–100 | Signal strength on an absolute scale, comparable across calls. It reflects how many distinct mentions carry the trend and how fast it’s moving. A higher score means a stronger signal. Null when the trend’s keywords could not be measured or when an older cached response has no score. Null does not mean zero. |
| `signals[].strength` | `"high" \| "medium" \| "low"` |  | Groups scored trends into three bands: high for 60–100, medium for 35–59, and low for 0–34. |
| `signals[].context` | `string` |  | A short explanation of the trend, grounded in conversation snippets retrieved and ranked for relevance to your query. |
| `signals[].keywords` | `string[]` |  | Related keywords within the trend. Use them to explore the language people use or build content briefs. Metrics for each keyword are in keywordStats. |
| `signals[].keywordStats` | `object[]` |  | Measurements and intent classifications for the related keywords, in the same order as keywords. Each entry names its phrase. This array may be empty for older cached responses. |
| `signals[].keywordStats[].phrase` | `string` |  | The keyword these measurements describe. |
| `signals[].keywordStats[].shareOfVoice` | `number \| null` | 0–100 | An estimate of how much of the relevant social conversation is associated with this keyword in the latest week of available data. Higher values mean it is more prominent around your query. A keyword without enough measurement data may return null; this does not mean zero. |
| `signals[].keywordStats[].growth` | `number \| null` | -100 to +100, 0 = flat | Whether the keyword is gaining or losing traction compared with recent weeks. Positive means rising, negative means declining, and 0 is roughly flat. This is a relative score, not a literal percentage change. Null means there is not enough measurement data, not zero growth. |
| `signals[].keywordStats[].intent` | `"commercial" \| "informational" \| null` |  | "commercial" for finding or buying a product, "informational" for learning or understanding, or null when no classification is available. Use it to choose keywords relevant to your workflow and guide the content you create. |
| `signals[].posts` | `object[]` |  | Supporting source posts, each with url and handle. |
| `signals[].posts[].url` | `string` |  | Link to the supporting post. |
| `signals[].posts[].handle` | `string` |  | The source account’s handle. |
| `read` | `string \| null` |  | A prose explanation of the trends, for format "text". Null for format "json". |
| `usage.cached` | `boolean` |  | True when served from cache. Cached responses do not consume a unit. |
| `usage.unitsRemaining` | `number` |  | Your remaining units after this call. |

### Example: emerging

```bash
curl -X POST https://gist-api.the-alt.co/v1/keywords/travel \
  -H "Authorization: Bearer $GIST_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query":"solo backpacking travellers","format":"json","mode":"emerging"}'
```

Response (first trend only; later trends and posts omitted):

```json
{
  "topics": ["travel"],
  "query": "solo backpacking travellers",
  "format": "json",
  "mode": "emerging",
  "knowledgeCutoff": "2026-10-04T23:59:59.999Z",
  "noSignal": false,
  "signals": [{
    "label": "Autumn Golden Larch Hiking",
    "strength": "medium",
    "score": null,
    "context": "Solo backpackers and hikers heading out during early October for peak fall foliage and larch season treks through mountain trails and national parks.",
    "keywords": ["golden larch", "early october", "late september", "during autumn", "#hiking"],
    "keywordStats": [
      { "phrase": "golden larch", "shareOfVoice": 2.2, "growth": 28.1, "intent": null },
      { "phrase": "early october", "shareOfVoice": 3.7, "growth": 81.8, "intent": null },
      { "phrase": "late september", "shareOfVoice": 3.7, "growth": 66.4, "intent": null },
      { "phrase": "during autumn", "shareOfVoice": 3.7, "growth": 70.4, "intent": null },
      { "phrase": "#hiking", "shareOfVoice": 3, "growth": 52.7, "intent": null }
    ],
    "posts": [
      { "url": "https://www.instagram.com/reel/Dd13NxjvbMX/", "handle": "herjoliejourney" }
    ]
  }],
  "read": null,
  "usage": { "cached": true, "unitsRemaining": 249 }
}
```

### Example: prevalent

```bash
curl -X POST https://gist-api.the-alt.co/v1/keywords/travel \
  -H "Authorization: Bearer $GIST_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query":"solo backpacking travellers","format":"json","mode":"prevalent"}'
```

Response (first trend only). Note `mountains` has negative growth:

```json
{
  "topics": ["travel"],
  "query": "solo backpacking travellers",
  "format": "json",
  "mode": "prevalent",
  "knowledgeCutoff": "2026-10-04T23:59:59.999Z",
  "noSignal": false,
  "signals": [{
    "label": "Alpine and Mountain Trekking",
    "strength": "high",
    "score": null,
    "context": "Solo backpackers and independent travelers heading into mountain trails, alpine passes, and rugged peaks with backpacks and hiking gear across Europe, the Americas, and Asia.",
    "keywords": ["mountains", "hiking", "mountain landscape", "alpine", "trails", "peaks"],
    "keywordStats": [
      { "phrase": "mountains", "shareOfVoice": 31.9, "growth": -3.8, "intent": null },
      { "phrase": "hiking", "shareOfVoice": 17.8, "growth": 8.9, "intent": null },
      { "phrase": "mountain landscape", "shareOfVoice": 7.4, "growth": 2.4, "intent": null },
      { "phrase": "alpine", "shareOfVoice": 9.6, "growth": 6.2, "intent": null },
      { "phrase": "trails", "shareOfVoice": 4.4, "growth": 1.3, "intent": null },
      { "phrase": "peaks", "shareOfVoice": 6.7, "growth": 3.5, "intent": null }
    ],
    "posts": [
      { "url": "https://www.instagram.com/p/Dd1bp1wMw6W/", "handle": "tom_juenemann" }
    ]
  }],
  "read": null,
  "usage": { "cached": true, "unitsRemaining": 249 }
}
```

## Answers

`POST /v1/search/{topic|all}`

Answers explains what people are saying about a question, with supporting posts. Ask about a trend discovered through Keywords, or bring your own question. Use the short `summary` or the fuller `read` as context in an AI tool or research workflow.

### Citations

`annotations` link passages in `read` to supporting posts. Each annotation has `start` and `end` zero-based Unicode character offsets into `read`: start is inclusive, end is exclusive. Apply offsets to the original text before formatting it. Multiple posts can support the same passage.

```js
// Unicode code points, not UTF-16 units — use Array.from, not read.slice
const chars = Array.from(response.read);
const citations = response.annotations.map((a) => ({
  passage: chars.slice(a.start, a.end).join(''),
  url: a.url,
  handle: a.handle,
}));
```

If there is not enough evidence to answer your question, the request succeeds with `noSignal: true`. Check `notes` for evidence gaps or limits, even when an answer is returned.

### Path parameters

| Parameter | Type | Notes | Description |
| --- | --- | --- | --- |
| `topic` | `string` | required | A coverage area you can access, such as "travel" or "recipes". Use "all" to have gist pick the most relevant topics your key can access for the query. |

### Body parameters

| Parameter | Type | Notes | Description |
| --- | --- | --- | --- |
| `query` | `string` | required | What you want to understand within the topic. A question or subject, up to 400 characters. |
| `geo` | `string` | optional | Filter by where authors or creators are based (max 120 characters). For example, "UAE" restricts results to UAE-based creators. More specific locations, such as "Dubai", are supported on a best-effort basis using creators’ stated locations. To explore a place as a subject, include it in query. |
| `weeks` | `integer` | default 4 | How many recent complete weeks the answer draws on (1–12). |
| `content` | `boolean` | default false | Also return contentRead, an explanation of how posts present the subject, such as formats, hooks and angles. |

### Response fields

| Field | Type | Notes | Description |
| --- | --- | --- | --- |
| `topics` | `string[]` |  | The topics searched. |
| `query` | `string` |  | Your query, echoed back. |
| `knowledgeCutoff` | `string` | ISO 8601 | The date and time through which the information behind the results is up to date. |
| `summary` | `string` |  | Short, prompt-ready synthesis. |
| `read` | `string` |  | The answer to your query, as clean prose. Citations are in annotations, not inline. |
| `noSignal` | `boolean` |  | True when there is not enough evidence to answer your question. This is a successful response, not an error. |
| `notes` | `string` |  | Useful context about the answer, including evidence gaps or limits. When noSignal is true, explains why. |
| `annotations` | `object[]` |  | Source posts for passages in read. start and end are character offsets: Array.from(read).slice(start, end).join("") returns the cited passage. Each annotation also includes url, platform and handle. Several posts can cite the same passage. |
| `annotations[].start` | `integer` |  | Zero-based start offset in the original read, inclusive. |
| `annotations[].end` | `integer` |  | End offset in the original read, exclusive. |
| `annotations[].url` | `string` |  | Link to the supporting post. |
| `annotations[].platform` | `string` |  | Source platform. May be an empty string when unavailable. |
| `annotations[].handle` | `string` |  | The source account’s handle. |
| `contentRead` | `string \| null` |  | How posts present the subject; null unless content:true was requested. |
| `usage.cached` | `boolean` |  | True when served from cache. Cached responses do not consume a unit. |
| `usage.unitsRemaining` | `number` |  | Your remaining units after this call. |

### Example

```bash
curl -X POST https://gist-api.the-alt.co/v1/search/travel \
  -H "Authorization: Bearer $GIST_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query":"solo backpacking travellers"}'
```

```json
{
  "topics": ["travel"],
  "query": "solo backpacking travellers",
  "knowledgeCutoff": "2026-10-04T23:59:59.999Z",
  "summary": "Solo travellers carrying backpacks frequently frame their journeys around personal transformation, intuitive departures, and emotional independence while managing lightweight gear and route logistics.",
  "read": "Solo travellers navigating destinations with a backpack consistently link unguided exploration to personal growth and emotional clarity. Solo journeys are often initiated impulsively or as a conscious step into independence, such as buying one-way tickets without concrete plans, choosing solo travel to process personal life transitions and seek quiet spaces, or taking trips specifically to relax and reset away from crowded itineraries. A recurring sentiment across these trips is finding self-reliance and peace when being alone in unfamiliar environments.\n\nAlongside the reflective focus, practical management centres on lightweight gear, mobility, and route logistics. Independent travellers emphasize streamlined packing to remain mobile, navigating transport like local buses or ferries, and implementing preventative security routines such as splitting cash and securing gear while moving between transit hubs. While some take their backpacks into rugged alpine hut networks or rural island trails, others adapt the setup to car-free towns or regional routes, leaning into minimal baggage to facilitate spontaneous movement.",
  "noSignal": false,
  "notes": "",
  "contentRead": null,
  "annotations": [
    { "start": 0, "end": 136, "url": "https://www.instagram.com/reel/DdB5qRCtJhR/", "platform": "", "handle": "@goanniewhere" },
    { "start": 0, "end": 136, "url": "https://www.instagram.com/reel/Dc_2jcHysoP/", "platform": "", "handle": "@mavuntamed" }
  ],
  "usage": { "cached": false, "unitsRemaining": 250 }
}
```

## Choose a topic

`GET /v1/topics`

Use this endpoint to find available topics. In the response's `topics` array, `topicName` is the display name, `topicId` is the name to use in your request URL, and `searchable: true` identifies topics ready for Keywords and Answers. Use `all` to have gist pick the most relevant topics your key can access for the query.

```bash
curl https://gist-api.the-alt.co/v1/topics -H "Authorization: Bearer $GIST_KEY"
```

## Request and response conventions

- Both endpoints use a topic slug in the path to select one area of coverage. Use `all` to have gist pick the most relevant topics your key can access for the query. The response's `topics` array shows which were searched.
- Use `geo` to filter by where authors or creators are based. For example, `"geo": "UAE"` restricts results to UAE-based creators. More specific locations, such as `"Dubai"`, are supported on a best-effort basis using creators’ stated locations. To explore a place as a subject, include it in `query`.
- `knowledgeCutoff` tells you how up to date the information behind the results is. For example, October 4 means the results draw on information through October 4.
- An uncached successful request costs 1 unit. A cached response costs 0 units. A response with `noSignal: true` is still successful and costs 1 unit unless cached. Check `usage.cached` and `usage.unitsRemaining` for each response. Repeating a request does not guarantee a cache hit.

### Handle a response

1. Check the HTTP status. For an error, follow the guidance below.
2. Check `noSignal` before displaying results; for Answers, also check `notes`.
3. Use the trends or answer, and retain supporting source links.
4. Keep `knowledgeCutoff` with the results and check the remaining units in `usage`.

## Errors

Check the HTTP status first. Application errors use this JSON envelope:

```json
{
  "error": {
    "code": "not_found",
    "message": "No current material for topic 'travel'."
  }
}
```

Authentication errors use a different shape. A missing or invalid key returns HTTP 401, for example:

```json
{
  "statusCode": 401,
  "message": "Provide a valid API key or sign in.",
  "error": "Unauthorized"
}
```

An account setup failure can also return HTTP 403 with `statusCode`, `message`, and `error` fields.

| Status | Code | When |
| --- | --- | --- |
| 400 | `invalid_request` | A parameter was malformed. |
| 401 | — | Missing, invalid, or revoked API key. |
| 403 | `forbidden` | You cannot read the topic you asked about. |
| 403 | `plan_required` | Your plan is inactive or does not provide access to this topic. |
| 404 | `not_found` | A topic you named does not exist or is not searchable yet. A topic that exists but can't answer returns 200 with noSignal: true, not 404. |
| 429 | `rate_limited` | Per-minute burst limit exceeded — retry shortly. |
| 429 | `quota_exceeded` | Credit budget exhausted for your plan (POST /v1/search, POST /v1/keywords). |
| 503 | `engine_unavailable` | The request could not be completed right now. Retry later. |

For `rate_limited` or `engine_unavailable`, wait and retry, increasing the delay between attempts. For `quota_exceeded`, replenish your units before retrying. For 400, 401, or 403, correct the request, credentials, or access issue first.
