> ## 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 Nephia Watch on Mastodon captures, 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 Watch 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 Watch can name.

A Watch therefore requires **exactly one of `hashtag` or `account`**, and takes no `query`. Sending one is a `400` rather than a criterion we would silently discard.

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

### 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–86400** seconds. Cap: **20** active Watches per Account. Each tick costs **1 credit**, and an `account` Watch 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. `webhook_url` is optional — without it the Watch still records every match, readable through `GET /v1/mastodon/watches/{id}/events`. See [Webhooks](/webhooks).

```bash theme={null}
curl -X POST https://api.nephia.cc/v1/mastodon/watches \
  -H "x-api-key: $NEPHIA_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "instance": "mastodon.social",
    "hashtag": "duckdb",
    "refresh_interval_seconds": 300,
    "webhook_url": "https://example.com/hooks/nephia"
  }'
```

Polling an account instead:

```bash theme={null}
curl -X POST https://api.nephia.cc/v1/mastodon/watches \
  -H "x-api-key: $NEPHIA_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "account": "@Mastodon@mastodon.social",
    "refresh_interval_seconds": 900
  }'
```

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 Watch reports it in its activity, readable through `GET /v1/mastodon/watches/{id}/activity`. An instance that refuses public timelines answers **403**, which is also not charged.

`instance` is validated as a hostname when the Watch is created, 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.
