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

# Twitter bookmark API to save a tweet by ID

> Bookmark 1 tweet by ID from a connected X account. Store the write action, terminal result and billed credits. Retry only when safeToRetry is true. 10 credits.

> [tab] 200
>
> ```json theme={null}
> {
>   "object": "x_write_action",
>   "id": "12345",
>   "writeActionId": "12345",
>   "action": "bookmark",
>   "status": "success",
>   "terminal": true,
>   "retryable": false,
>   "safeToRetry": false,
>   "statusUrl": "/api/v1/x/write-actions/12345",
>   "pollAfterMs": null
> }
> ```

> [tab] 202
>
> ```json theme={null}
> {
>   "object": "x_write_action",
>   "id": "12346",
>   "writeActionId": "12346",
>   "action": "bookmark",
>   "status": "dispatching",
>   "terminal": false,
>   "retryable": false,
>   "safeToRetry": false,
>   "statusUrl": "/api/v1/x/write-actions/12346",
>   "pollAfterMs": 2000
> }
> ```

> [tab] 400
>
> ```json theme={null}
> {
>   "error": "missing_idempotency_key",
>   "message": "Idempotency-Key is required. Generate one unique key for this write.",
>   "charged": false,
>   "chargedCredits": "0",
>   "retryable": false,
>   "safeToRetry": true
> }
> ```

> [tab] 401
>
> ```json theme={null}
> {
>   "error": "unauthenticated",
>   "message": "Authentication required. Provide a valid API key or bearer token."
> }
> ```

> [tab] 402
>
> ```json theme={null}
> {
>   "error": "insufficient_credits",
>   "message": "Insufficient credits. Top up or subscribe to continue."
> }
> ```

> [tab] 403
>
> ```json theme={null}
> {
>   "error": "api_key_paused",
>   "message": "This API key is paused. Resume it at dashboard.xquik.com/api-keys to use it again."
> }
> ```

> [tab] 404
>
> ```json theme={null}
> {
>   "error": "account_not_found",
>   "message": "X account not found. Connect it first at /dashboard/x-accounts."
> }
> ```

> [tab] 409
>
> ```json theme={null}
> {
>   "error": "idempotency_conflict",
>   "message": "Idempotency-Key was already used with a different request.",
>   "charged": false,
>   "chargedCredits": "0",
>   "retryable": false,
>   "safeToRetry": true
> }
> ```

> [tab] 422
>
> ```json theme={null}
> {
>   "error": "x_rejected",
>   "message": "X rejected this request. Check what you sent & the account on x.com before you try again."
> }
> ```

> [tab] 429
>
> ```json theme={null}
> {
>   "error": "x_rate_limited",
>   "message": "X rate limited this account. Try again in about 15 minutes. Send fewer requests from this account."
> }
> ```

> [tab] 500
>
> ```json theme={null}
> {
>   "error": "x_write_failed",
>   "message": "Write action failed unexpectedly. Contact support if this persists."
> }
> ```

> [tab] 503
>
> ```json theme={null}
> {
>   "error": "write_tracking_unavailable",
>   "message": "Write tracking unavailable. Try again.",
>   "charged": false,
>   "chargedCredits": "0",
>   "retryable": true,
>   "safeToRetry": true
> }
> ```

> For the complete documentation index, see [llms.txt](https://docs.xquik.com/llms.txt).

**10 credits per call** · [All plans](https://xquik.com) from \$0.00012/credit

Each request bookmarks 1 tweet by ID.
Each request uses 1 connected X account.
Only that account sees its bookmarks.
Create a unique `Idempotency-Key` for the intended bookmark.
Store the returned write action and poll `statusUrl` when needed.

```bash cURL theme={null}
curl -X POST https://xquik.com/api/v1/x/tweets/1895432178065391234/bookmark \
  -H "x-api-key: xq_YOUR_KEY_HERE" \
  -H "Idempotency-Key: bookmark-1895432178065391234" \
  -H "Content-Type: application/json" \
  -d '{"account":"elonmusk"}' | jq '{
    status,
    terminal,
    safe_to_retry: .safeToRetry,
    write_action_id: .id,
    requested_tweet_id: "1895432178065391234",
    confirmed_tweet_id: (.result.id // .targetId),
    charged_credits: .billing.chargedCredits,
    poll_path: (if .terminal then null else .statusUrl end)
  }'
```

```javascript Node.js theme={null}
const tweetId = "1895432178065391234";
const response = await fetch(`https://xquik.com/api/v1/x/tweets/${tweetId}/bookmark`, {
  method: "POST",
  headers: {
    "x-api-key": "xq_YOUR_KEY_HERE",
    "Idempotency-Key": "bookmark-1895432178065391234",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ account: "elonmusk" }),
});
const result = await response.json();
if (!response.ok) {
  throw new Error(JSON.stringify(result));
}
const bookmarkRecord = {
  status: result.status,
  terminal: result.terminal,
  safe_to_retry: result.safeToRetry,
  write_action_id: result.id,
  requested_tweet_id: tweetId,
  confirmed_tweet_id: result.result?.id ?? result.targetId ?? null,
  charged_credits: result.billing.chargedCredits,
  poll_path: result.terminal ? null : result.statusUrl,
};
process.stdout.write(`${JSON.stringify(bookmarkRecord)}\n`);
```

```python Python theme={null}
import json
import requests

tweet_id = "1895432178065391234"
response = requests.post(
    f"https://xquik.com/api/v1/x/tweets/{tweet_id}/bookmark",
    headers={
        "x-api-key": "xq_YOUR_KEY_HERE",
        "Idempotency-Key": "bookmark-1895432178065391234",
    },
    json={"account": "elonmusk"},
)
result = response.json()
response.raise_for_status()
bookmark_record = {
    "status": result["status"],
    "terminal": result["terminal"],
    "safe_to_retry": result["safeToRetry"],
    "write_action_id": result["id"],
    "requested_tweet_id": tweet_id,
    "confirmed_tweet_id": (result.get("result") or {}).get("id") or result.get("targetId"),
    "charged_credits": result["billing"]["chargedCredits"],
    "poll_path": None if result["terminal"] else result["statusUrl"],
}
print(json.dumps(bookmark_record))
```

## Bookmark one tweet by ID

Put the exact Tweet ID in the request path.
Put the connected account username or ID in the body.
That account saves the bookmark.

Copy Tweet IDs as strings.
JavaScript numbers cannot represent every X identifier exactly.
This endpoint accepts 1 Tweet ID per request.

## Keep bookmarks private to the acting account

X shows a bookmark only to the account that saved it.
The post's author sees a bookmark count, never who saved it.
Bookmarking never notifies the author or adds a like.

Read the saved posts with [Bookmarks](https://docs.xquik.com/api-reference/x/bookmarks.md).

## Save a bookmark receipt

Save the action first. Then update your reading list.

| Bookmark action column | Source | Rule |
| - | - | - |
| `acting_account` | Request `account` | Keep the connected X account with the bookmark. |
| `tweet_id` | Path `id` | Keep the exact Tweet ID the user picked. |
| `idempotency_key` | Request header | Generate 1 key for this account and Tweet ID. |
| `write_action_id` | Response `id` | Store the action before polling. |
| `terminal` | Response `terminal` | Mark the bookmark saved only when `true`. |
| `safe_to_retry` | Response `safeToRetry` | Retry only when this value is `true`. |

## Choose bookmark, like, or read bookmarks

| Intent | API route | Result |
| - | - | - |
| Save one tweet privately | `POST /x/tweets/{id}/bookmark` | Adds the post to the account's bookmarks. |
| Remove one saved tweet | `DELETE /x/tweets/{id}/bookmark` | Removes the post from the bookmarks. |
| Read saved tweets | `GET /x/bookmarks` | Returns the account's bookmarked posts. |
| Like one tweet in public | `POST /x/tweets/{id}/like` | Adds a public like instead. |

Use [Remove bookmark](https://docs.xquik.com/api-reference/x-write/unbookmark.md) to reverse a bookmark.

## Twitter bookmark API questions

### Can an API bookmark a tweet by ID?

Yes. Send the Tweet ID in the path and the connected account in the body.

### Can other people see the bookmark?

No. X shows bookmarks only to the account that saved them.
The author sees the total bookmark count on the post.

## Handle errors

Never infer failure from a client timeout.
Poll a returned action before sending another request.

* For `400`, correct the Tweet ID or account value.
* For `401`, replace invalid Xquik authentication.
* For `402`, add credits before another request.
* For `403`, reconnect the selected X account.
* For `409`, keep the original action and payload.
* For `422`, review the X rejection before retrying.
* For `429`, wait for `Retry-After`.
* For `500` or `503`, follow `safeToRetry` and `terminal`.

## Headers

**x-api-key** `string`

Send your Xquik API key in this header. Alternatively, send an OAuth 2.1 bearer token.

**Authorization** `string`

Send `Bearer <token>` instead of `x-api-key` when using OAuth 2.1.

**Idempotency-Key** `string` *required*

Unique key for this intended write. Reuse it only for an exact network replay.

**Content-Type** `string` *required*

Must be `application/json`.

## Path parameters

**id** `string` *required*

Post ID or URL-encoded post URL, such as `x.com/nasa/status/20`. See [path IDs](https://docs.xquik.com/api-reference/overview.md#path-ids).

## Body

**account** `string` *required*

X username or account ID identifying which connected X account will bookmark the tweet. Xquik strips the `@` prefix if included.

## Response

## Durable write recovery

> [!WARNING]
>
> Send one unique `Idempotency-Key` per intended write.
> Replay the same account, target, payload, and media after a lost response.
> Keep the original key for that replay.

1. Store `id`, the nested `hash` in `request`, `billing`, and `statusUrl`.
2. Poll after `Retry-After` or `pollAfterMs` when `terminal` is `false`.
3. Retry only when `safeToRetry` is `true`.
4. Use a new key when `nextAction.requiresNewIdempotencyKey` is `true`.

### 200 terminal or 202 active

* After HTTP `200`, store the result and settled billing.
* After HTTP `202`, poll the same action. Never submit another write.
* After HTTP `400`, fix the named field. Use a new idempotency key.
* After HTTP `401`, fix authentication. Do not retry unchanged.
* After HTTP `402`, fund the account before another write.
* After HTTP `403`, reconnect the account.
* After HTTP `409`, keep the original action. Use a new key for new input.
* After HTTP `422`, fix the rejected request before retrying.
* After HTTP `429`, wait for `Retry-After`. Follow `nextAction`.

See [Get Write Action Status](https://docs.xquik.com/api-reference/x-write/get-write-action-status.md)
for every lifecycle field and terminal state. That page also lists every billing
field and retry rule.


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