> ## 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 like API: like a tweet by ID with status

> Like one 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.

<Panel>
  <Tabs defaultTabIndex={0} sync={false}>
    <Tab title="200" id="response-x-write-like-200">
      ```json theme={null}
      {
        "object": "x_write_action",
        "id": "12345",
        "writeActionId": "12345",
        "action": "like",
        "status": "success",
        "terminal": true,
        "retryable": false,
        "safeToRetry": false,
        "statusUrl": "/api/v1/x/write-actions/12345",
        "pollAfterMs": null
      }
      ```
    </Tab>

    <Tab title="202" id="response-x-write-like-202">
      ```json theme={null}
      {
        "object": "x_write_action",
        "id": "12346",
        "writeActionId": "12346",
        "action": "like",
        "status": "dispatching",
        "terminal": false,
        "retryable": false,
        "safeToRetry": false,
        "statusUrl": "/api/v1/x/write-actions/12346",
        "pollAfterMs": 2000
      }
      ```
    </Tab>

    <Tab title="400" id="response-x-write-like-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>

    <Tab title="401" id="response-x-write-like-401">
      ```json theme={null}
      {
        "error": "unauthenticated",
        "message": "Authentication required. Provide a valid API key or bearer token."
      }
      ```
    </Tab>

    <Tab title="402" id="response-x-write-like-402">
      ```json theme={null}
      {
        "error": "insufficient_credits",
        "message": "Insufficient credits. Top up or subscribe to continue."
      }
      ```
    </Tab>

    <Tab title="403" id="response-x-write-like-403">
      ```json theme={null}
      {
        "error": "account_needs_reauth",
        "message": "X account needs re-authentication. Re-add the account."
      }
      ```
    </Tab>

    <Tab title="404" id="response-x-write-like-404">
      ```json theme={null}
      {
        "error": "account_not_found",
        "message": "X account not found. Connect it first at /dashboard/account?tab=x-accounts."
      }
      ```
    </Tab>

    <Tab title="409" id="response-x-write-like-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>

    <Tab title="422" id="response-x-write-like-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>

    <Tab title="429" id="response-x-write-like-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>

    <Tab title="500" id="response-x-write-like-500">
      ```json theme={null}
      {
        "error": "x_write_failed",
        "message": "Write action failed unexpectedly. Contact support if this persists."
      }
      ```
    </Tab>

    <Tab title="503" id="response-x-write-like-503">
      ```json theme={null}
      {
        "error": "write_tracking_unavailable",
        "message": "Write tracking unavailable. Try again.",
        "charged": false,
        "chargedCredits": "0",
        "retryable": true,
        "safeToRetry": true
      }
      ```
    </Tab>
  </Tabs>
</Panel>

<blockquote className="agent-llms-directive">
  For the complete documentation index, see <a href="/llms.txt">llms.txt</a>.
</blockquote>

<Callout icon="coins" color="#5c3327">
  **10 credits per call** · [All plans](https://xquik.com/#pricing) from \$0.00012/credit
</Callout>

Each request likes 1 tweet by ID.
Each request uses 1 connected X account.
Save user approval before sending the request.
Create a unique `Idempotency-Key` for the intended like.
Store the returned write action and poll `statusUrl` when needed.
Confirm the returned target matches the requested Tweet ID.

X documents its separate [Like Post endpoint](https://docs.x.com/x-api/users/like-post).
X says not to auto-like or bulk-like Tweets in its [Developer Guidelines](https://docs.x.com/developer-guidelines).
Keep every like user-initiated.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://xquik.com/api/v1/x/tweets/1895432178065391234/like \
    -H "x-api-key: xq_YOUR_KEY_HERE" \
    -H "Idempotency-Key: like-1895432178065391234" \
    -H "Content-Type: application/json" \
    -d '{
      "account": "elonmusk"
    }' | jq '{
      status,
      terminal,
      safe_to_retry: .safeToRetry,
      write_action_id: .id,
      request_hash: .request.hash,
      requested_tweet_id: "1895432178065391234",
      confirmed_tweet_id: (.result.id // .tweetId // .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}/like`, {
    method: "POST",
    headers: {
      "x-api-key": "xq_YOUR_KEY_HERE",
      "Idempotency-Key": "like-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 likeRecord = {
    status: result.status,
    terminal: result.terminal,
    safe_to_retry: result.safeToRetry,
    write_action_id: result.id,
    request_hash: result.request.hash,
    requested_tweet_id: tweetId,
    confirmed_tweet_id: result.result?.id ?? result.tweetId ?? result.targetId ?? null,
    account: result.account,
    charged_credits: result.billing.chargedCredits,
    poll_path: result.terminal ? null : result.statusUrl,
  };
  process.stdout.write(`${JSON.stringify(likeRecord)}\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}/like",
      headers={
          "x-api-key": "xq_YOUR_KEY_HERE",
          "Idempotency-Key": "like-1895432178065391234",
      },
      json={
          "account": "elonmusk",
      },
  )
  result = response.json()
  response.raise_for_status()
  like_record = {
      "status": result["status"],
      "terminal": result["terminal"],
      "safe_to_retry": result["safeToRetry"],
      "write_action_id": result["id"],
      "request_hash": result["request"]["hash"],
      "requested_tweet_id": tweet_id,
      "confirmed_tweet_id": (
          (result.get("result") or {}).get("id")
          or result.get("tweetId")
          or result.get("targetId")
      ),
      "account": result["account"],
      "charged_credits": result["billing"]["chargedCredits"],
      "poll_path": None if result["terminal"] else result["statusUrl"],
  }
  print(json.dumps(like_record))
  ```

  ```go Go theme={null}
  package main

  import (
      "bytes"
      "encoding/json"
      "fmt"
      "io"
      "net/http"
  )

  type LikeTweetResponse struct {
      ID string `json:"id"`
      Status string `json:"status"`
      Terminal bool `json:"terminal"`
      SafeToRetry bool `json:"safeToRetry"`
      StatusURL string `json:"statusUrl"`
      TargetID *string `json:"targetId"`
      TweetID *string `json:"tweetId"`
      Request struct {
          Hash string `json:"hash"`
      } `json:"request"`
      Account map[string]any `json:"account"`
      Billing struct {
          ChargedCredits string `json:"chargedCredits"`
      } `json:"billing"`
      Result *struct {
          ID string `json:"id"`
      } `json:"result"`
  }

  func main() {
      tweetID := "1895432178065391234"
      body, err := json.Marshal(map[string]string{
          "account": "elonmusk",
      })
      if err != nil {
          panic(err)
      }

      req, err := http.NewRequest(
          "POST",
          "https://xquik.com/api/v1/x/tweets/"+tweetID+"/like",
          bytes.NewReader(body),
      )
      if err != nil {
          panic(err)
      }
      req.Header.Set("x-api-key", "xq_YOUR_KEY_HERE")
      req.Header.Set("Idempotency-Key", "like-1895432178065391234")
      req.Header.Set("Content-Type", "application/json")

      resp, err := http.DefaultClient.Do(req)
      if err != nil {
          panic(err)
      }
      defer resp.Body.Close()
      if resp.StatusCode >= 400 {
          responseBody, _ := io.ReadAll(resp.Body)
          panic(string(responseBody))
      }

      var result LikeTweetResponse
      if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
          panic(err)
      }
      var confirmedTweetID any
      if result.Result != nil && result.Result.ID != "" {
          confirmedTweetID = result.Result.ID
      } else if result.TweetID != nil {
          confirmedTweetID = *result.TweetID
      } else if result.TargetID != nil {
          confirmedTweetID = *result.TargetID
      }
      var pollPath any
      if !result.Terminal {
          pollPath = result.StatusURL
      }
      likeRecord := map[string]any{
          "status": result.Status,
          "terminal": result.Terminal,
          "safe_to_retry": result.SafeToRetry,
          "write_action_id": result.ID,
          "request_hash": result.Request.Hash,
          "requested_tweet_id": tweetID,
          "confirmed_tweet_id": confirmedTweetID,
          "account": result.Account,
          "charged_credits": result.Billing.ChargedCredits,
          "poll_path": pollPath,
      }
      encoded, err := json.Marshal(likeRecord)
      if err != nil {
          panic(err)
      }
      fmt.Println(string(encoded))
  }
  ```
</CodeGroup>

## Like 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 performs the like.

Copy Tweet IDs as strings.
JavaScript numbers cannot represent every X identifier exactly.
Keep the selected Tweet ID beside the approving user.

This endpoint accepts 1 Tweet ID per request.
It does not fetch Tweets, create reposts, or publish replies.
Use [Get Tweet](/api-reference/x/get-tweet) before showing an approval screen.

## Keep every like user-initiated

Require a clear user choice before each like.
Show the tweet text, author, and Tweet ID during approval.
Store who approved the like and when.

Do not build auto-like, bulk-like, or purchased-like workflows.
Do not select scraped tweets and like them without user approval.
Only send the like that your user picked.

A review queue can prepare candidates without performing actions.
Your user must choose the exact tweet before this endpoint runs.
Keep the approval reference in the action ledger.

## Authenticate the connected X account

Send an Xquik API key or OAuth 2.1 bearer token.
Then identify the connected X account with `account`.
The request never accepts an X password or session cookie.

Keep Xquik credentials in server-side code.
Do not expose them in mobile apps or browser bundles.
Reconnect that X account when you receive `account_needs_reauth`.

The official X endpoint uses a user access token.
Its authentication differs from the Xquik contract.
Read X's [Like Post guide](https://docs.x.com/x-api/users/like-post) for that endpoint.

## Save a like receipt

An HTTP response alone does not prove the write.
Save the action first. Then update your like record.
Keep the request and response identities together.

| Like action column | Source | Engagement rule |
| - | - | - |
| `acting_account` | Request `account` | Keep the connected X account with the engagement. |
| `tweet_id` | Path `id` | Use the stable Tweet ID selected by review. |
| `approval_reference` | Workflow value | Store the user's approval ID with this like. |
| `idempotency_key` | Request header | Generate one key for this account and Tweet ID. |
| `write_action_id` | Response `id` | Store the action before polling. |
| `status` | Response `status` | Keep queued, completed, and failed likes distinct. |
| `terminal` | Response `terminal` | Mark the like complete only when `true`. |
| `safe_to_retry` | Response `safeToRetry` | Retry when this value is `true`. |
| `requested_at` | Integration timestamp | Record when the user approved this like. |

Store these response fields:

* Store `id` as the write action ID.
* Store `request.hash` for exact request matching.
* Store `account` for the connected X account.
* Store `targetId` or `result.id` for the liked Tweet ID.
* Store `status`, `terminal`, and `statusUrl` for polling.
* Store `billing.chargedCredits` for the settled charge.
* Store `safeToRetry` before considering another request.

## Poll pending likes and verify completion

A `200` response contains a terminal action.
A `202` response means the like remains active.
Poll `statusUrl` until `terminal` becomes `true`.

Do not submit another like while the first action remains active.
After success, confirm the result matches the requested Tweet ID.
Treat an already-liked terminal result as success.

Use [User Likes](/api-reference/x/user-likes) only when current read proof matters.
Store the read time separately from the action time.
X hides most likes from other accounts. A read may not confirm the like.

| Like outcome | Evidence | Ledger update |
| - | - | - |
| Queued | `terminal: false` | Keep the row pending and poll `statusUrl`. |
| Completed | Terminal success | Record the completed like for this account and Tweet ID. |
| Already liked | Terminal result that reports an existing like | Complete without submitting another like. |
| Retryable failure | `safeToRetry: true` | Keep the receipt. Follow its retry fields. |
| Final failure | Terminal failure | Store the error and keep the ledger unchanged. |

## Prevent duplicate tweet likes

Create 1 idempotency key for the intended account and Tweet ID.
Reuse that key only after an interrupted response.
Never reuse it for a different account or tweet.

An HTTP `409` means the key already belongs to another payload.
Keep the original action and inspect its request hash.
Create a new key only for a new user-approved intent.

The idempotency key stops duplicate submissions to Xquik.
Still check approval, terminal status, and the acting account.

## Handle Twitter like API errors

Never infer failure from a client timeout.
Poll a returned action before sending another request.
Follow `safeToRetry` when the outcome is unclear.

* For `400`, correct the Tweet ID or account value.
* For `401`, replace invalid Xquik authentication.
* For `402`, add credits before another like.
* 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`.

X publishes separate limits for its [Like Post endpoint](https://docs.x.com/x-api/fundamentals/rate-limits).
Those limits do not replace Xquik's response headers.
Never increase throughput by creating a bulk-like queue.

## Choose like, unlike, or read likes

Use this route only to add a like from one connected account.
Use [Unlike Tweet](/api-reference/x-write/unlike) to reverse that account's like.
Use read endpoints when no engagement action is needed.

| Engagement intent | API route | Result |
| - | - | - |
| Like one tweet | `POST /x/tweets/{id}/like` | Adds the connected account's like. |
| Unlike one tweet | `DELETE /x/tweets/{id}/like` | Removes the connected account's like. |
| Read a user's liked tweets | `GET /x/users/{id}/likes` | Returns that user's available liked tweets. |
| Read accounts that liked a tweet | `GET /x/tweets/{id}/favoriters` | Returns available liking accounts. |
| Repost one tweet | `POST /x/tweets/{id}/retweet` | Creates a separate repost action. |

Liking does not create a repost, reply, bookmark, or follow.
Approve each action separately.

## Twitter like API questions

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

Yes. Send the Tweet ID in this route's path.
Send the connected acting account in the JSON body.
Keep the like user-initiated.

### Does this endpoint bulk-like tweets?

No. It accepts 1 Tweet ID per request.
X prohibits bulk-like and auto-like behavior.
Do not loop through candidates without individual user approval.

### How does Twitter like API authentication work?

Authenticate to Xquik with an API key or OAuth 2.1 bearer token.
Select a previously connected X account with `account`.
Never send an X password.

### How should a Twitter API like tweet retry work?

Store the action first.
Poll while `terminal` is `false`.
Retry only when `safeToRetry` is `true`.

### Can the same API like, repost, and reply?

Xquik documents separate routes for each action.
Use [Retweet](/api-reference/x-write/retweet) for a repost.
Use [Create Tweet](/api-reference/x-write/create-tweet) for a reply.

### Which libraries can like a tweet?

Any HTTP client can call this REST endpoint.
The examples cover cURL, Node.js, Python, and Go.
Keep credentials in server-side code.

## Headers

<ParamField header="x-api-key" type="string">
  Send your Xquik API key in this header. Alternatively, send an OAuth 2.1 bearer token.
</ParamField>

<ParamField header="Authorization" type="string">
  Send `Bearer <token>` instead of `x-api-key` when using OAuth 2.1.
</ParamField>

<ParamField header="Idempotency-Key" type="string" required>
  Unique key for this intended write. Reuse it only for an exact network replay.
</ParamField>

<ParamField header="Content-Type" type="string" required>
  Must be `application/json`.
</ParamField>

## Path parameters

<ParamField path="id" type="string" required>
  Post ID or URL-encoded post URL, such as `x.com/nasa/status/20`. See [path IDs](/api-reference/overview#path-ids).
</ParamField>

## Body

<ParamField body="account" type="string" required>
  X username or account ID identifying which connected X account will like the tweet. Xquik strips the `@` prefix if included.
</ParamField>

## 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.
</Warning>

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](/api-reference/x-write/get-write-action-status)
for every lifecycle field, terminal state, billing field, and retry rule.


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