Tweet Archive
Free · No API key · No account

For AI agents

If you're an AI agent or writing a script, you can archive a tweet directly through the API below instead of using the web form — no key, no account, just an HTTP request.

Archive a tweet

POST /api/v1/tweets — body {"urls": [...]}, 1–10 tweet URLs or bare numeric IDs per request.

curl -X POST https://x-dot-com-archive.vercel.app/api/v1/tweets \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://x.com/user/status/1234567890"]}'

Returns a JSON array, same order as the input. Each entry is either a tweet (shape below) or {"input": "...", "error": "..."} — one bad item never fails the rest of the batch. Archiving happens in the background after the response returns, so snapshots may still be empty right away. Poll the status endpoint below until one appears; it usually completes within about a minute.

Without a token, text over ~280 characters gets trimmed (the free syndication API this service reads by default has the same limit X's own oEmbed widget does). If you have your own X API access, pass it as x_bearer_token — it costs nothing extra for a normal-length tweet (only spent when the free result is actually truncated), and it means the archive is complete instead of cut off, for anyone who looks it up later. agent (self-identify for attribution) is the other optional field. Both are documented in the skill file below.

Check archive status

GET /api/v1/tweets/{id} — read-only, no side effects, safe to poll repeatedly. 404 if the ID has never been submitted.

curl https://x-dot-com-archive.vercel.app/api/v1/tweets/1234567890

Archive a thread or reply context

POST /api/v1/tweets/{id}/thread/check and POST /api/v1/tweets/{id}/thread/ingest — pull in the tweets around {id}, not just that one tweet.

curl -X POST https://x-dot-com-archive.vercel.app/api/v1/tweets/1234567890/thread/check \
  -H "Content-Type: application/json" \
  -d '{"x_bearer_token": "<optional>"}'

Ancestors (what {id} is replying to) are always free, no key, in both calls — walked backward one tweet at a time via the free API. Forward (the author's own later tweets in that conversation) needs x_bearer_token: check pays for exactly one X API search call to find the author's latest reply, then walks backward from it for free — so discovering a whole thread costs about one search call regardless of length, not one call per tweet. check returns a free preview (every discovered tweet, and whether it's individually truncated) plus a real cost estimate for upgrading the truncated ones. ingest actually archives everything — calling it at all is the confirmation, no separate confirm flag. Full request/response shapes are in the skill file below.

Response shape

{
  "id": "2095401070080184644",
  "status": "live",
  "tweet": { "...raw syndication payload..." },
  "truncated": false,
  "note": "present only when truncated — explains how to get the full text",
  "archive_url": "https://x-dot-com-archive.vercel.app/tweets/2095401070080184644",
  "first_archived_at": "2026-09-01T09:00:00.000Z",
  "removed_at": null,
  "removed_snapshot": null,
  "snapshots": [
    {
      "wayback_url": "https://web.archive.org/web/.../...",
      "archived_at": "2026-09-07T15:21:22.000Z",
      "screenshot_url": "https://.../....png",
      "event": "edited"
    }
  ]
}

snapshots is newest-first — snapshots[0].wayback_url is the permanent archive link, and each entry's event is "created" for the first archived version or "edited" for every later one. status is "live" or "removed" (deleted from X since archiving — the archive itself still exists), in which case removed_at and removed_snapshot point at when and which archived version was live right before that. No auth header, no rate limiting yet — batch up to 10 URLs per request rather than looping single-item calls.

Skill file & OpenAPI spec

A ready-to-use SKILL.md describes this API for agent frameworks that load skill files. A machine-readable /openapi.json (OpenAPI 3.1) works with GPT Actions and similar tool-calling frameworks — useful since a plain ChatGPT conversation's built-in browsing is read-only and can't submit the POST itself; registering this spec as a Custom GPT Action lets it actually call the API. Also see /llms.txt for a plain-text summary of the whole site.

---
name: tweet-archive
description: Archive a tweet (X post) permanently, free, no API key required. POST a tweet URL to Tweet Archive's public API to trigger a Wayback Machine snapshot, then poll for the result. Use when asked to archive, preserve, back up, or save a tweet before it might be deleted, or to check whether a tweet has already been archived.
---

# Tweet Archive

[Tweet Archive](https://x-dot-com-archive.vercel.app) is a free,
unauthenticated service that captures a permanent archive.org (Wayback
Machine) snapshot of a tweet, plus a screenshot, so the tweet survives
even if it's later deleted from X.

## Archive a tweet

```
POST https://x-dot-com-archive.vercel.app/api/v1/tweets
Content-Type: application/json

{"urls": ["<tweet URL or bare numeric ID>", ...]}
```

1 to 10 items per request.

```bash
curl -X POST https://x-dot-com-archive.vercel.app/api/v1/tweets \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://x.com/user/status/1234567890"]}'
```

Returns a JSON array, same order as the input `urls`. Each entry is
either a tweet object (see shape below) or `{"input": "...", "error":
"..."}` for anything unparseable or unfetchable. One bad item never fails
the rest of the batch.

Archiving happens in the background after the response returns;
`snapshots` may still be empty right away. Poll the status endpoint below
every few seconds until a snapshot appears; it usually completes within
about a minute.

**Without a token, text over ~280 characters gets trimmed.** This
service reads X's free public syndication API by default, which
truncates long tweets the same way X's own oEmbed widget does. If you
have your own X API access, pass it: it costs nothing extra for a
normal-length tweet (see below), and it means the tweet gets archived in
full instead of truncated, a better permanent record for anyone who
looks it up later, not just for you.

Two optional top-level fields, applying to the whole request:
- `x_bearer_token`: your own X API Bearer Token, for full-length
  tweets. Only spent when actually needed: the free API always runs
  first, and your token is only used if that result is truncated or the
  free call fails outright, so a normal-length tweet costs you nothing
  even if you supply one. You'll need your own X developer access to
  have a token; this service never provides or pays for one.
- `agent`: a name to self-identify as (e.g. `"my-bot/1.0"`), recorded
  on the archive for attribution. Falls back to your User-Agent header if
  omitted.

## Archive a whole thread or reply context

Instead of archiving just the one tweet, you can pull in the tweets
around it: what it's replying to (free), and/or the author's own
continuation tweets (needs your own X API access).

```
POST https://x-dot-com-archive.vercel.app/api/v1/tweets/{id}/thread/check
POST https://x-dot-com-archive.vercel.app/api/v1/tweets/{id}/thread/ingest
Content-Type: application/json

{"x_bearer_token": "<optional>"}
```

`check` is free preview, `ingest` actually archives -- calling `ingest`
at all is the confirmation, there's no separate confirm flag.

- **Ancestors** (what `{id}` is replying to): always free, no key, in
  both calls. Walked backward one tweet at a time via the free API.
- **Forward** (the author's own later tweets in that conversation): only
  with `x_bearer_token`. `check` pays for exactly *one* X API search call
  to find the author's latest reply in the conversation, then walks
  backward from it for free -- so discovering a whole thread costs about
  one search call regardless of how long the thread is, not one call per
  tweet. `check`'s response includes a full preview (every discovered
  tweet's text, and whether it's individually truncated) plus a real
  `estimated_full_text_cost_usd` for upgrading the truncated ones -- no
  money is spent beyond that one search call until you call `ingest`.
  `ingest` then upgrades each truncated tweet to full text one at a time
  (pass `"upgrade_truncated": false` to skip that and just archive them
  truncated, or `"max_cost_usd"` to cap how much the upgrade step can
  spend). Only covers the last 7 days of replies unless your token has a
  pricier full-archive search tier.

`check`'s response: `{"id", "ancestors": [...], "is_reply", "heuristic":
{"conversation_count", "looks_like_thread"}, "forward": {...}}`.
`ingest`'s response: `{"id", "ancestors": [...], "forward": {"tweets":
[...], "upgraded_count", "conversation_id"} | null,
"skipped_forward_reason": "no_token" | "budget_exhausted" | null}`. Every
tweet entry in both is the same shape as the response below.

## Check archive status

```
GET https://x-dot-com-archive.vercel.app/api/v1/tweets/{id}
```

Read-only, no side effects; safe to poll repeatedly. Returns 404 if the
ID has never been submitted.

## Response shape (one tweet)

```json
{
  "id": "2095401070080184644",
  "status": "live",
  "tweet": { "...raw syndication payload..." },
  "truncated": false,
  "note": "present only when truncated, explains how to get the full text",
  "archive_url": "https://x-dot-com-archive.vercel.app/tweets/2095401070080184644",
  "first_archived_at": "2026-09-01T09:00:00.000Z",
  "removed_at": null,
  "removed_snapshot": null,
  "snapshots": [
    {
      "wayback_url": "https://web.archive.org/web/.../...",
      "archived_at": "2026-09-07T15:21:22.000Z",
      "screenshot_url": "https://.../....png",
      "event": "edited"
    }
  ]
}
```

- `snapshots` is newest-first: `snapshots[0].wayback_url` is the
  permanent archive link to hand back to whoever asked.
- `status` is `"live"` or `"removed"` (deleted from X since being
  archived; the archive itself still exists in `snapshots`).
- `tweet` is `null` once a tweet has been removed.
- `truncated` is `true` when the stored text is a cut-off preview.
- `first_archived_at` is when we first archived this tweet -- not the
  tweet's own creation time on X (that's inside `tweet`, if not removed).
- `snapshots[].event` is `"created"` for the tweet's first archived
  version, `"edited"` for every later one whose content actually changed.
- `removed_at`/`removed_snapshot` are set once the tweet is found removed
  (`removed_snapshot` is the archived version that was live right before
  that, `snapshots[0]`); both `null` while live. `removed_at` can be
  `null` even when `status` is `"removed"` for tweets removed before this
  field existed -- the removal time just isn't known for those.

## Notes

- No API key, no account, no auth header required.
- No rate limiting is enforced yet; be a reasonable citizen: batch up
  to 10 URLs per request instead of looping single-item calls.
- Machine-readable OpenAPI 3.1 spec (for GPT Actions and similar
  tool-calling frameworks): https://x-dot-com-archive.vercel.app/openapi.json
- Full docs: https://x-dot-com-archive.vercel.app/agents