Create Monitor API
Start watching an X account. Every new post from that account is delivered, HMAC-signed, to your registered webhook(s) on a shared poll interval. Free: monitor creation is account administration, not a metered read. Cost: Free per call.
POST monitor creates an account-level monitor that watches a given X handle and delivers every new post to your webhook(s) via HMAC-signed HTTP callbacks on a shared poll interval. It is free to create. An optional domain_filter narrows delivery to only the new posts that link to a given host (or its subdomains); posts filtered out this way still advance the monitor's cursor, they are just not delivered. The response returns the monitor's id, normalized handle, subject expression, kind, status, webhook_ids, domain_filter, poll_interval_ms, and created_at.
Authorization
bearerAuth Pass your API key as a bearer token on every request: Authorization: Bearer <API_KEY>.
In: header
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/monitor" \ -H "Content-Type: application/json" \ -d '{ "handle": "elonmusk" }'{ "id": "b6f2c1a4-9e3d-4c11-8a2f-7d5e6c9b0a11", "handle": "elonmusk", "subject": "from:elonmusk", "kind": "account", "status": "active", "webhook_ids": null, "domain_filter": null, "poll_interval_ms": 120000, "poll_interval_note": "NOT a published SLA yet, subject to change.", "created_at": "2026-08-13T10:00:00.000Z"}{ "error": "bad_request", "message": "Missing or malformed parameter. Fix the request before retrying."}{ "error": "unauthorized", "message": "The API key is missing, malformed, or revoked. Check the Authorization header."}{ "error": "insufficient_credits", "message": "Your balance is exhausted. Top up your credits to continue."}{ "error": "forbidden", "message": "The acting account is not authorized for this write action, or has no logged-in session."}{ "error": "not_found", "message": "The resource does not exist, for example a deleted tweet or a private account."}{ "error": "rate_limited", "message": "Too many requests. Back off and retry with exponential backoff."}{ "error": "server_error", "message": "Something failed on our side. Retry with backoff; if it persists, contact support."}Pricing
| Unit | Price |
|---|---|
| Per call | Free |
When to use
Use this endpoint to start watching a specific X account so every new post it publishes is pushed to your webhook(s) automatically, instead of polling a search or timeline endpoint yourself. Reach for it when you need push-based, near-real-time delivery of one account's new posts rather than an on-demand read of existing content. Add domain_filter when you only care about posts that link to one particular site, for example tracking an account's own blog or product links rather than every post it makes.
Notes
- Refused with 503 capacity_unavailable if the shared pool cannot currently serve one more monitor at the published interval, rather than silently accepting and serving it slower.
- Refused with 403 customer_slot_limit_reached if this account is already at the flat per-account monitor cap. There is no priced plan yet, so this is a shared stopgap limit, not a plan-specific slot ceiling.
- Create a webhook first (POST /twitter/webhook) if you want to restrict delivery via webhook_ids; otherwise every active webhook on the account receives every event.
- domain_filter accepts either a bare hostname or a full URL; both normalize to the same bare hostname. "example.com" matches a linked host of exactly example.com or any subdomain like blog.example.com, but never notexample.com or example.com.evil.net (matched by hostname boundary, never substring).
- A post with no matching link is filtered out of delivery, not silently dropped: it still counts toward the account's tweets_domain_filtered health counter, so filtered volume stays visible.
- 400 bad_request with message invalid domain filter "<raw>": must be a bare hostname or URL like "example.com" or "https://example.com/blog" if what's left after normalization does not look like a real hostname.
Related endpoints
- List Monitors: List every monitor on your account.
- Update Monitor: Pause, resume, or change which webhooks a monitor delivers to.
FAQ
Do I have to specify webhook_ids when creating a monitor?
No, webhook_ids is optional. If you omit it, new posts from the watched handle are delivered to every active webhook on your account. Pass an array of webhook ids only when you want delivery restricted to a subset of your registered webhooks.
How do I know how often the monitor checks for new posts, and can it stop delivering?
The create response includes poll_interval_ms, the current poll interval in milliseconds, which runs on a shared interval across monitors rather than a fixed constant. Also check the status field: it is active or paused, and a monitor only delivers posts to webhooks while active.
Does creating a monitor cost anything?
No. POST monitor has a cost of 0, so creating an account monitor is free regardless of how many webhooks receive the deliveries.
What does domain_filter actually restrict?
It narrows DELIVERY, not detection: the monitor still detects every new post from the watched handle, but only delivers the ones carrying at least one URL entity whose expanded hostname equals your filter or is a subdomain of it. Posts with no matching link still advance the monitor's cursor and are counted in the tweets_domain_filtered health metric, they are just not pushed to your webhook(s).
Can I pass a full URL instead of a bare domain?
Yes. "https://example.com/blog" and "example.com" both normalize to the same stored value: scheme, path, query, fragment, a leading www., and a trailing port are all stripped before the hostname-shape check runs.
Delete Webhook DELETE
Soft-deletes the webhook: it stops receiving deliveries immediately and disappears from List Webhooks, but delivery history referencing it is retained rather than cascade-deleted. Cost: Free per call.
List Monitors GET
Returns every non-deleted monitor on your account, most recently created first. Each entry includes its id, subject, status, and delivery configuration, so you can enumerate what already exists before creating, updating, or deleting a specific monitor. Cost: Free per call.