# X (Twitter) (/docs/platforms/twitter)



Connecting an account [#connecting-an-account]

X connects with X sign-in (OAuth 2.0). Open **Connections** in the dashboard, or send a connect link (`POST /api/v1/platform-invites` with `"platform": "twitter"`, see [Connect links](/docs/guides/connect-links)).

FeedHorizon asks X for `tweet.read`, `tweet.write`, `users.read` and `offline.access`. The last one lets FeedHorizon renew the connection automatically, so it keeps working until the user revokes FeedHorizon in their X settings. Then the account turns `needs_reauth` and the `account.needs_reauth` webhook is sent.

The platform id in the API is `twitter`.

What you can post [#what-you-can-post]

|                | Limit                                                                                             |
| -------------- | ------------------------------------------------------------------------------------------------- |
| Text           | 280 characters                                                                                    |
| Files per post | up to 4 files, or none                                                                            |
| Images         | JPEG, PNG or GIF                                                                                  |
| Videos         | MP4                                                                                               |
| File size      | Up to 100 MB by URL import or 5 GB by upload. The platform may refuse large files when publishing |

Each post is a single post. Threads (a chain of replies) are not supported yet.

Example [#example]

Give X a shorter text than the other networks with `targets`:

```bash title="cURL"
curl -X POST https://feedhorizon.com/api/v1/posts \
  -H "Authorization: Bearer fh_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "profileId": "prof_…",
    "content": "We just shipped scheduled exports. Every report can now land in your inbox on a fixed day, as CSV or PDF.",
    "targets": [
      { "accountId": "acc_LINKEDIN" },
      { "accountId": "acc_X", "content": "Scheduled exports are live: reports as CSV or PDF, on a fixed day." }
    ],
    "publishNow": true
  }'
```

After publishing [#after-publishing]

The X target carries the post id in `platformPostId` and the link in `platformUrl` (`https://x.com/i/web/status/<id>`).

Errors [#errors]

Checked before the post is stored (`400`, see [Error handling](/docs/api/errors)):

| Code                     | Cause                    | Fix                                                   |
| ------------------------ | ------------------------ | ----------------------------------------------------- |
| `content_too_long`       | More than 280 characters | Shorten the text, or give X its own text in `targets` |
| `too_many_media`         | More than 4 files        | Remove files                                          |
| `media_type_unsupported` | For example WebP or MOV  | Convert to JPEG/PNG/GIF or MP4                        |

While publishing (in `targets[].error`, see [Publishing errors](/docs/api/errors#publishing-errors)). Rate limits and outages are retried automatically:

| Code                   | Cause                                                                                            | Fix                               |
| ---------------------- | ------------------------------------------------------------------------------------------------ | --------------------------------- |
| `account_needs_reauth` | The connection was revoked                                                                       | Reconnect the account, then retry |
| `platform_auth_failed` | X refused the token or a permission is missing                                                   | Reconnect the account, then retry |
| `platform_rejected`    | X rejected the post, for example as a duplicate of a recent post. `error.message` has X's reason | Change the content, then retry    |
