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

# Mastodon

> What a keyword captures on Mastodon, and what arrives in each event.

## The status

Mastodon's content unit is a **Status**, not a post and not a "toot": the API's own endpoint, entity and field are all called `status`, and `post` belongs to Reddit throughout this API.

`content` is **plain text**. The API serves an HTML fragment; Nephia strips it, because the same body is rendered in a webhook, in a feed row and in an AI prompt, and shipping markup would push a sanitising problem into all three.

`author.acct` is always fully qualified and lowercased: `alice@mastodon.social`, never a bare `alice`. Mastodon reports a local account bare and a remote one qualified, so the same person would otherwise be two different authors depending on which instance a search happened to read.

`instance` is the host the status was **read from**, which is not necessarily the author's home server.

## Coverage

**Mastodon has no global keyword search, and this Source does not pretend otherwise.** Full-text search on Mastodon is opt-in per user *and* per instance and requires an authenticated account token; a public instance serves neither. What it does serve (to anyone, documented, unauthenticated) is a hashtag timeline and an account's statuses. Those are the two things a search can name.

A search therefore requires **exactly one of `hashtag` or `account`**, and takes no `query`: a Mastodon search that resolves to a search text is a **400** rather than a criterion we would silently discard, so keep the keyword's `globalCriteria` free of `query` and `terms` when Mastodon is enabled. These are this Source's own criteria: on the keyword they go in the search's `overrides.filters` (see [Keywords](/keywords#create)).

| Criterion | Values | Default |
| - | - | - |
| `instance` | a hostname | `mastodon.social` |
| `hashtag` | a tag, without the `#` | none |
| `account` | `@user@host`, or a bare `user` local to `instance` | none |

### What one instance can see

A Mastodon server shows you its **federated view**: the statuses its own users follow or that have otherwise reached it. A large general instance sees a great deal; a small topical one sees its own corner. Watching `#yourbrand` on `mastodon.social` is not the same as watching the whole network, and no endpoint offers the whole network.

If a conversation about you lives on one particular server, watch that server.

**Boosts are skipped.** A boost is not a mention: the original reaches the same timeline on its own, so keeping boosts would report one status once per person who shared it, and fire a webhook for each.

Interval: **120 to 86400** seconds. At most **20** active Mastodon searches per Account. Each tick costs **1 credit**, and an `account` search costs the same as a `hashtag` one even though it makes two requests: a lookup to turn the handle into an id, then the timeline.

## What an event carries

The first tick establishes a silent baseline and emits nothing. Later ticks emit `status.created` with the full status. `webhookUrl` is optional: without it the keyword still records every match, readable through `GET /v1/keywords/{id}/events` in the same shape. See [Webhooks](/webhooks#replay).

```bash theme={null}
curl -X POST https://api.nephia.cc/v1/keywords \
  -H "x-api-key: $NEPHIA_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "name": "#duckdb on Mastodon",
    "refreshIntervalSeconds": 300,
    "globalCriteria": { "market": "global" },
    "sources": [
      {
        "source": "mastodon",
        "enabled": true,
        "searches": [
          {
            "overridesEnabled": true,
            "overrides": {
              "filters": {
                "instance": "mastodon.social",
                "hashtag": "duckdb"
              }
            }
          }
        ]
      }
    ],
    "webhookUrl": "https://example.com/hooks/nephia"
  }'
```

Polling an account instead:

```bash theme={null}
curl -X POST https://api.nephia.cc/v1/keywords \
  -H "x-api-key: $NEPHIA_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "name": "Mastodon official account",
    "refreshIntervalSeconds": 900,
    "globalCriteria": { "market": "global" },
    "sources": [
      {
        "source": "mastodon",
        "enabled": true,
        "searches": [
          {
            "overridesEnabled": true,
            "overrides": {
              "filters": {
                "account": "@Mastodon@mastodon.social"
              }
            }
          }
        ]
      }
    ]
  }'
```

A tick narrows with `since_id` and also dedupes by id, so a status that federates in late still arrives exactly once. On a network of independent servers, arriving late is normal rather than exceptional.

## Errors

An unreachable or misbehaving instance is **503** and is not charged; the keyword reports it in its activity, readable through `GET /v1/keywords/{id}/activity`. An instance that refuses public timelines answers **403**, which is also not charged.

`instance` is validated as a hostname when the keyword is written, and the address it resolves to is checked again on every request: a host that resolves to a private or link-local address is refused.


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