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

# Integration Guide

> This guide covers the key concepts you need to integrate the Post lookup endpoints into your. Reference for the X API v2 standard tier covering lookup.

export const Button = ({href, children}) => {
  return <div className="not-prose group">
    <a href={href}>
      <button className="flex items-center space-x-2.5 py-1 px-4 bg-primary-dark dark:bg-white text-white dark:text-gray-950 rounded-full group-hover:opacity-[0.9] font-medium">
        <span>
          {children}
        </span>
        <svg width="3" height="24" viewBox="0 -9 3 24" class="h-6 rotate-0 overflow-visible"><path d="M0 0L3 3L0 6" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"></path></svg>
      </button>
    </a>
  </div>;
};

This guide covers the key concepts you need to integrate the Post lookup endpoints into your application.

***

## Authentication

All X API v2 endpoints require authentication. Choose the method that fits your use case:

| Method                                                                                                                         | Best for                      | Can access private metrics?       |
| :----------------------------------------------------------------------------------------------------------------------------- | :---------------------------- | :-------------------------------- |
| [OAuth 2.0 App-Only](/resources/fundamentals/authentication#oauth-2-0)                                                         | Server-to-server, public data | No                                |
| [OAuth 2.0 Authorization Code with PKCE](/resources/fundamentals/authentication#oauth-2-0-authorization-code-flow-with-pkce-2) | User-facing apps              | Yes (for authorized user's Posts) |
| [OAuth 1.0a User Context](/resources/fundamentals/authentication)                                                              | Legacy integrations           | Yes (for authorized user's Posts) |

### App-Only authentication

For public Post data, use a Bearer Token:

<CodeGroup dropdown>
  ```bash cURL theme={null}
  curl "https://api.x.com/2/tweets/1234567890" \
    -H "Authorization: Bearer $BEARER_TOKEN"
  ```

  ```python Python SDK theme={null}
  from xdk import Client

  client = Client(bearer_token="YOUR_BEARER_TOKEN")

  # Get a single Post by ID
  response = client.posts.get("1234567890")
  print(response.data)
  ```

  ```javascript JavaScript SDK theme={null}
  import { Client } from "@xdevplatform/xdk";

  const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" });

  const response = await client.posts.get("1234567890");
  console.log(response.data);
  ```
</CodeGroup>

### User Context authentication

To access private metrics, authenticate on behalf of the Post author:

<Warning>
  The following fields require User Context authentication:

  * `tweet.fields.non_public_metrics`
  * `tweet.fields.promoted_metrics`
  * `tweet.fields.organic_metrics`
  * `media.fields.non_public_metrics`
  * `media.fields.promoted_metrics`
  * `media.fields.organic_metrics`
</Warning>

***

## Fields and expansions

The X API v2 returns minimal data by default. Use `fields` and `expansions` to request exactly what you need.

### Default response

```json theme={null}
{
  "data": {
    "id": "1234567890",
    "text": "Hello world!",
    "edit_history_tweet_ids": ["1234567890"]
  }
}
```

### Available fields

<Accordion title="tweet.fields">
  | Field                 | Description                        |
  | :-------------------- | :--------------------------------- |
  | `created_at`          | Post creation timestamp            |
  | `author_id`           | Author's user ID                   |
  | `public_metrics`      | Like, retweet, reply, quote counts |
  | `entities`            | Hashtags, mentions, URLs, cashtags |
  | `attachments`         | Media keys, poll IDs               |
  | `conversation_id`     | Thread identifier                  |
  | `context_annotations` | Topic/entity classifications       |
  | `in_reply_to_user_id` | User being replied to              |
  | `lang`                | Detected language                  |
  | `source`              | Posting client                     |
  | `possibly_sensitive`  | Sensitive content flag             |
  | `reply_settings`      | Who can reply                      |
</Accordion>

<Accordion title="user.fields (requires author_id expansion)">
  | Field               | Description               |
  | :------------------ | :------------------------ |
  | `username`          | @handle                   |
  | `name`              | Display name              |
  | `profile_image_url` | Avatar URL                |
  | `verified`          | Verification status       |
  | `description`       | Bio                       |
  | `public_metrics`    | Follower/following counts |
  | `created_at`        | Account creation date     |
</Accordion>

<Accordion title="media.fields (requires attachments.media_keys expansion)">
  | Field               | Description                 |
  | :------------------ | :-------------------------- |
  | `url`               | Media URL                   |
  | `preview_image_url` | Thumbnail URL               |
  | `type`              | photo, video, animated\_gif |
  | `duration_ms`       | Video duration              |
  | `height`, `width`   | Dimensions                  |
  | `alt_text`          | Accessibility text          |
</Accordion>

### Example with fields

<CodeGroup dropdown>
  ```bash cURL theme={null}
  curl "https://api.x.com/2/tweets/1234567890?\
  tweet.fields=created_at,public_metrics,entities&\
  expansions=author_id,attachments.media_keys&\
  user.fields=username,verified&\
  media.fields=url,type" \
    -H "Authorization: Bearer $BEARER_TOKEN"
  ```

  ```python Python SDK theme={null}
  from xdk import Client

  client = Client(bearer_token="YOUR_BEARER_TOKEN")

  # Get a Post with additional fields and expansions
  response = client.posts.get(
      "1234567890",
      tweet_fields=["created_at", "public_metrics", "entities"],
      expansions=["author_id", "attachments.media_keys"],
      user_fields=["username", "verified"],
      media_fields=["url", "type"]
  )

  print(response.data)
  print(response.includes)  # Contains expanded user and media objects
  ```

  ```javascript JavaScript SDK theme={null}
  import { Client } from "@xdevplatform/xdk";

  const client = new Client({ bearerToken: "YOUR_BEARER_TOKEN" });

  const response = await client.posts.get("1234567890", {
    tweetFields: ["created_at", "public_metrics", "entities"],
    expansions: ["author_id", "attachments.media_keys"],
    userFields: ["username", "verified"],
    mediaFields: ["url", "type"],
  });

  console.log(response.data);
  console.log(response.includes); // Contains expanded user and media objects
  ```
</CodeGroup>

***

## Post edits

Posts can be edited up to 5 times within 30 minutes of creation.

### How it works

* Each edit creates a new Post ID
* `edit_history_tweet_ids` contains all versions (oldest first)
* The endpoint always returns the most recent version

### Example response

```json theme={null}
{
  "data": {
    "id": "1234567893",
    "text": "Hello world! (edited twice)",
    "edit_history_tweet_ids": [
      "1234567890",
      "1234567891",
      "1234567893"
    ]
  }
}
```

<Tip>
  Posts retrieved after their 30-minute edit window represent the final version. For real-time use cases, be aware that recently-published Posts may still be edited.
</Tip>

***

## Error handling

### Common errors

| Status | Error             | Solution                          |
| :----- | :---------------- | :-------------------------------- |
| 400    | Invalid request   | Check parameter formatting        |
| 401    | Unauthorized      | Verify authentication credentials |
| 403    | Forbidden         | Check App permissions             |
| 404    | Not Found         | Post deleted or doesn't exist     |
| 429    | Too Many Requests | Wait and retry (see rate limits)  |

### Deleted or protected Posts

If a Post is deleted or from a protected account you don't follow:

* Single Post lookup returns `404`
* Multi-Post lookup omits the Post from results with an `errors` array

```json theme={null}
{
  "data": [
    { "id": "1234567890", "text": "Available post" }
  ],
  "errors": [
    {
      "resource_id": "1234567891",
      "resource_type": "tweet",
      "title": "Not Found Error",
      "detail": "Could not find tweet with id: [1234567891]."
    }
  ]
}
```

***

## Best practices

<CardGroup cols={2}>
  <Card title="Batch requests" icon="layer-group">
    Use the multi-Post endpoint to fetch up to 100 Posts at once, reducing API calls.
  </Card>

  <Card title="Request only needed fields" icon="filter">
    Specify only the fields you need to minimize response size and processing time.
  </Card>

  <Card title="Cache responses" icon="database">
    Cache Post data locally to reduce repeated requests for the same content.
  </Card>

  <Card title="Handle edits" icon="clock-rotate-left">
    For real-time apps, consider re-fetching Posts after the 30-minute edit window.
  </Card>
</CardGroup>

***

## Next steps

<CardGroup cols={2}>
  <Card title="API Reference" icon="code" href="/x-api/posts/post-lookup-by-post-id">
    Complete endpoint documentation
  </Card>

  <Card title="Data dictionary" icon="book" href="/x-api/fundamentals/data-dictionary">
    All available objects and fields
  </Card>

  <Card title="Sample code" icon="github" href="https://github.com/xdevplatform/Twitter-API-v2-sample-code">
    Working code examples
  </Card>

  <Card title="Error handling" icon="triangle-exclamation" href="/x-api/fundamentals/response-codes-and-errors">
    Handle errors gracefully
  </Card>
</CardGroup>
