> **For coding agents and LLMs:** This is one Social Fetch marketing page (markdown export). For API orientation start with [`/llms.txt`](https://www.socialfetch.dev/llms.txt); for auth and crawl rules use [`/agents.txt`](https://www.socialfetch.dev/agents.txt); for every operation use [`/llms-endpoints.txt`](https://www.socialfetch.dev/llms-endpoints.txt).

## This page

- **On-site (HTML):** <https://www.socialfetch.dev/use-cases/twitter-webhook-api>
- **Markdown (.mdx) URL:** <https://www.socialfetch.dev/use-cases/twitter-webhook-api.mdx>

---

# Get a webhook when a Twitter account posts

Monitor polls, diffs, and delivers a signed webhook on new tweets. You write the handler; we run schedule, diff, and retries.

X's official streaming access sits behind enterprise tiers. Nitter instances and scrapers break without retries, delivery logs, or verifiable signatures.

Monitors polls a profile, diffs results, and delivers a signed webhook (or hosted test inbox). Checks bill at the underlying lookup rate — no monitor markup.

## Endpoints

| Method | Path | What it does |
| --- | --- | --- |
| GET | `/v1/monitors/sources` | Discover watchable sources, including twitter.profile.tweets.list, its minimum polling interval, and a realistic sample event for that source. |
| POST | `/v1/webhook-endpoints` | Create the delivery target: a real HTTPS URL (kind: "http"), or a zero-setup hosted test inbox (kind: "sink") if you just want to see a signed event first. |
| POST | `/v1/monitors` | Create the monitor: operationId "twitter.profile.tweets.list", the handle in params, an interval or cron schedule, and the webhook endpoint to deliver to. Add ?dryRun=true to preview the credit cost first. |
| POST | `/v1/monitors/{id}/trigger` | Force an immediate check instead of waiting for the schedule — useful while you're building and testing your webhook handler. |
| GET | `/v1/monitors/{id}/events` | Pull new-tweet events directly, on your own schedule — a valid pull-only alternative if you'd rather not run a webhook receiver at all. |
| GET | `/v1/webhook-endpoints/{id}/deliveries` | Delivery log for one endpoint: every attempt, its headers, its raw payload, and the response you sent back — the same log socialfetch listen tails for local testing. |

## Workflow

1. **Discover the source and its minimum interval.** Call GET /v1/monitors/sources and find twitter.profile.tweets.list. Note minIntervalMinutes — that is the fastest this source will poll (currently 5 minutes).
2. **Create a webhook endpoint — or start with a test inbox.** POST /v1/webhook-endpoints with { "kind": "sink" } gets you a hosted inbox with no URL to deploy. Swap it for a real { "kind": "http", "url": "..." } endpoint once your handler works.
3. **Preview the cost, then create the monitor.** POST /v1/monitors?dryRun=true returns perCheckCredits, perDayCredits, and perMonthCredits for your chosen interval before anything is created or charged. Drop dryRun to create for real — it also runs a synchronous baseline so you don't get an event for tweets that already existed.
4. **Verify the signature over the raw body.** The socialfetch-signature header is HMAC-SHA256 over the exact request bytes. Use verifySignature or constructEvent from @socialfetch/sdk/webhooks, and read the raw body before any JSON parsing happens in your framework.
5. **Dedupe on event id, not delivery id.** Retries and manual redeliveries carry the same socialfetch-event-id as the original attempt. Key your dedup logic off that, not the per-attempt socialfetch-delivery-id.
6. **Watch it work before you deploy anything.** npx socialfetch listen --forward-to http://localhost:3000/... forwards real, signed deliveries to your machine. Trigger a check in a second terminal and watch a genuine signed webhook arrive within seconds.

## Create a monitor on a Twitter/X profile, delivering to a webhook endpoint you already created.

```bash
curl -sS \
  -H "x-api-key: YOUR_API_KEY" \
  -H "content-type: application/json" \
  -X POST "https://api.socialfetch.dev/v1/monitors" \
  -d '{
    "operationId": "twitter.profile.tweets.list",
    "params": { "handle": "elonmusk" },
    "schedule": { "type": "interval", "minutes": 15 },
    "webhookEndpointId": "whe_yourEndpointId"
  }'

# Add ?dryRun=true to the URL above to preview the credit cost
# without creating anything or running the baseline check.
```

## Why teams use Monitors instead of Nitter, RSS bridges, or a homemade poller

- No dead Nitter instance to babysit — Monitors calls our own maintained Twitter endpoint, the same one every other Social Fetch integration uses.
- HMAC-signed like the webhook systems you already trust: Stripe's t=…,v1=… scheme verbatim, verified with a small SDK helper, not a hand-rolled signature check.
- 8 delivery attempts over roughly 21 hours on failure, every attempt logged with headers, payload, and response — you're not silently missing tweets because your server had a bad minute.
- A hosted test inbox and a socialfetch listen CLI mean you can see a real signed event before you've deployed a single line of handler code.
- Per-check pricing at the same rate the tweets lookup already costs — no monitor markup, no discount for a check that finds nothing new, no separate management fee for creating or updating the monitor.
- If your webhook handler goes down, the monitor keeps checking and recording events; pull them back with the events API or redeliver manually once you're up.
