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

# List Xquik tweet drafts with cursor-based pagination

> List Xquik tweet drafts with exact text, optional topic and goal, timestamps, stable cursor pagination, authentication, and clear 400, 401, or 429 recovery.

<Panel>
  <Tabs defaultTabIndex={0} sync={false}>
    <Tab title="200" id="response-drafts-list-200">
      ```json theme={null}
      {
        "drafts": [],
        "hasMore": false
      }
      ```
    </Tab>

    <Tab title="400" id="response-drafts-list-400">
      ```json theme={null}
      {
        "error": "invalid_input",
        "message": "Invalid input. Check the request body."
      }
      ```
    </Tab>

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

    <Tab title="429" id="response-drafts-list-429">
      ```json theme={null}
      {
        "error": "rate_limit_exceeded",
        "message": "Too many requests. Try again later.",
        "retryAfter": 60
      }
      ```
    </Tab>
  </Tabs>
</Panel>

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

## List saved tweet drafts

List tweet drafts stored in your authenticated Xquik account. Each result
contains saved text, an optional topic, an optional goal, and timestamps.

Use this route to build a draft inventory or review queue. It returns drafts
newest first and supports cursor pagination. The route never publishes draft
text or sends it to followers.

Xquik drafts are separate from [X's native Unsent posts](https://help.x.com/en/using-x/how-to-post).
This API cannot list drafts saved inside the X compose interface.

<Callout icon="circle-check" color="#16a34a">
  **Free.** This endpoint does not consume credits.
</Callout>

<CodeGroup>
  ```bash cURL theme={null}
  curl --fail-with-body \
    "https://xquik.com/api/v1/drafts?limit=20" \
    --header "x-api-key: xq_YOUR_KEY_HERE" | jq
  ```

  ```javascript Node.js theme={null}
  const response = await fetch("https://xquik.com/api/v1/drafts?limit=20", {
    headers: {
      "x-api-key": "xq_YOUR_KEY_HERE",
    },
  });
  const page = await response.json();
  if (!response.ok) {
    throw new Error(`${response.status} ${page.error}: ${page.message}`);
  }

  for (const draft of page.drafts) {
    console.log(draft.id, draft.text, draft.createdAt);
  }
  ```

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

  response = requests.get(
      "https://xquik.com/api/v1/drafts",
      headers={"x-api-key": "xq_YOUR_KEY_HERE"},
      params={"limit": 20},
  )
  page = response.json()
  if response.status_code != 200:
      raise RuntimeError(
          f'{response.status_code} {page["error"]}: {page["message"]}'
      )

  for draft in page["drafts"]:
      print(draft["id"], draft["text"], draft["createdAt"])
  ```

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

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

  func main() {
      req, err := http.NewRequest("GET", "https://xquik.com/api/v1/drafts?limit=20", nil)
      if err != nil {
          panic(err)
      }
      req.Header.Set("x-api-key", "xq_YOUR_KEY_HERE")

      resp, err := http.DefaultClient.Do(req)
      if err != nil {
          panic(err)
      }
      defer resp.Body.Close()

      var page map[string]interface{}
      if err := json.NewDecoder(resp.Body).Decode(&page); err != nil {
          panic(err)
      }
      if resp.StatusCode != http.StatusOK {
          panic(fmt.Sprintf("%d %v: %v", resp.StatusCode, page["error"], page["message"]))
      }

      fmt.Println(page["drafts"], page["hasMore"])
  }
  ```
</CodeGroup>

## Understand the tweet draft list

The response contains one `drafts` array and pagination fields. An empty array
is a successful result. It means the account has no saved drafts.

Drafts use descending creation order. A newer `createdAt` value appears first.
The draft ID breaks ties between equal creation times. This order does not
change while clients follow returned cursors.

The list contains Xquik draft records only. It excludes published tweets,
scheduled posts, native X drafts, and drafts owned by another account.

Each draft contains one saved text string. The draft object contains
no thread order, media attachment, reply target, or public tweet ID.

## Paginate through every draft

Request up to 50 drafts per page. The default and maximum `limit` are both 50.
A smaller value helps clients process short review batches.

Follow this cursor workflow:

1. Request the first page without `afterCursor`.
2. Process every draft in the returned order.
3. Check `hasMore` after processing the page.
4. Copy `nextCursor` when `hasMore` is `true`.
5. Send that value as the next `afterCursor`.
6. Stop when `hasMore` becomes `false`.

Treat `nextCursor` as an opaque value. Never decode, edit, or construct it.
Keep only the latest cursor after a page succeeds.

The final page omits `nextCursor`. Do not expect an empty string or `null`.
Use `hasMore` as the loop condition.

This route exposes no topic, goal, text, date, or status filter. Filter the
returned draft objects inside your client when you need a smaller review set.

Do not paginate with timestamps or guessed draft IDs. Those values cannot
replace the returned cursor. A cursor from another account also cannot select
that account's drafts.

## Read the draft inventory fields

The API omits optional fields that you did not supply during draft creation.
Check for `topic` and `goal` before reading them.

| Field | Meaning | Inventory use |
| - | - | - |
| `drafts[].id` | Stable Xquik draft ID | Retrieve or delete the exact draft. |
| `drafts[].text` | Exact saved tweet text | Review the proposed post copy. |
| `drafts[].topic` | Optional composition topic | Group drafts by intended subject. |
| `drafts[].goal` | Optional composition goal | Review engagement, followers, authority, or conversation intent. |
| `drafts[].createdAt` | ISO 8601 creation timestamp | Sort or record intake time. |
| `drafts[].updatedAt` | ISO 8601 update timestamp | Compare a result with a cached copy. |
| `hasMore` | Whether another page exists | Continue or stop pagination. |
| `nextCursor` | Opaque cursor for the next page | Pass it through unchanged. |

The `text` field can contain up to 25,000 characters. The optional `topic`
field can contain up to 500 characters. A `goal` can be `engagement`,
`followers`, `authority`, or `conversation`.

## Build a tweet draft review queue

Use list pagination before retrieval, publishing, or cleanup.

1. List the newest Xquik tweet drafts.
2. Store each returned draft ID once.
3. Review its text, topic, goal, and timestamps.
4. Retrieve one draft again before final approval.
5. Publish approved text through a separate X write route.
6. Delete obsolete drafts after required review.
7. Continue until `hasMore` is `false`.

Listing a draft does not reserve, approve, publish, or delete it. Your client
must record those workflow states separately.

The list response provides no draft count across every page. Count processed
IDs locally when an inventory total matters. Deduplicate by `id` during long
runs where other clients create or delete drafts.

Use [Get Draft](/api-reference/drafts/get) before you delete drafts. Use
[Create Tweet](/api-reference/x-write/create-tweet) only after content approval.

## Keep native X drafts separate

Native X drafts appear under Unsent posts inside the compose interface. Native
thread drafts may contain several connected posts. Native drafts may also
contain photos, GIFs, or video.

An Xquik draft stores one text value and optional composition context. It does
not mirror the native X draft collection.

| Draft source | Listed here? | Correct action |
| - | - | - |
| Xquik Create Draft API | Yes | Paginate this endpoint. |
| Xquik Get Draft API | Yes | Match the same Xquik draft ID. |
| X Unsent posts | No | Open the native X compose interface. |
| Published tweet or thread | No | Use an X tweet read endpoint. |
| Scheduled post | No | Use the matching scheduling workflow. |

Do not send a public tweet ID as `afterCursor`. Public tweet IDs and draft
cursors identify different resources.

## Recover from draft list errors

| Status | Error | Cause | Fix |
| - | - | - | - |
| `200` | Draft page | The authenticated request succeeded | Process `drafts`, then inspect `hasMore`. |
| `400` | `invalid_input` | `afterCursor` is edited or unknown | Resend the last `nextCursor`, or omit it. |
| `401` | `unauthenticated` | The credential is missing or invalid | Replace the API key or bearer token. |
| `429` | `rate_limit_exceeded` | Too many requests reached the route | Wait for `Retry-After`, then retry once. |

An empty `drafts` array is not a `404`. It is a valid `200` response.

Never retry `401` without replacing the credential. Retry `429` only after the
server's delay. Keep the last successful cursor before retrying a page.

## Tweet draft list questions

### How can I list my saved tweet drafts?

Call `GET /drafts` with an API key or OAuth bearer token. The response lists
drafts owned by that authenticated Xquik account.

### Does this API list native Twitter or X drafts?

No. Native drafts remain under Unsent posts. This route lists records created
through the Xquik draft API.

### How many tweet drafts can one page return?

One page returns at most 50 drafts. Use `nextCursor` when `hasMore` is `true`.

### Can I search draft text or filter by topic?

No server-side search or filter parameter exists. Paginate, then filter fields
inside your client.

### Does one draft include a thread or media?

No. One result contains a text string and optional composition context. It
contains no thread sequence, media collection, or reply target.

### Are deleted drafts included?

No. Deleted drafts are absent. Xquik provides no deleted-draft restore route.

### Does listing tweet drafts consume credits?

No. This authenticated list request is free and consumes no Xquik credits.

## Query parameters

<ParamField query="limit" type="number">
  Results per page. Default `50`, maximum `50`.
</ParamField>

<ParamField query="afterCursor" type="string">
  Opaque cursor from `nextCursor`. Pass it unchanged to fetch the next page. An edited or unknown cursor returns `400`.
</ParamField>

## Headers

<ParamField header="x-api-key" type="string">
  Your Xquik API key. Generate one from the [dashboard](https://xquik.com/dashboard).
</ParamField>

<ParamField header="Authorization" type="string">
  An OAuth bearer token formatted as `Bearer YOUR_TOKEN`. Send this header or `x-api-key`, not both.
</ParamField>

## Response

### 200 OK

<ResponseField name="drafts" type="object[]">
  Draft objects ordered by creation time, newest first.
</ResponseField>

<ResponseField name="drafts[].id" type="string">
  Unique Xquik draft ID.
</ResponseField>

<ResponseField name="drafts[].text" type="string">
  Exact saved tweet text.
</ResponseField>

<ResponseField name="drafts[].topic" type="string">
  Optional draft topic. Omitted when unset.
</ResponseField>

<ResponseField name="drafts[].goal" type="string">
  Optional composition goal. Omitted when unset.
</ResponseField>

<ResponseField name="drafts[].createdAt" type="string">
  ISO 8601 creation timestamp.
</ResponseField>

<ResponseField name="drafts[].updatedAt" type="string">
  ISO 8601 update timestamp.
</ResponseField>

<ResponseField name="hasMore" type="boolean">
  Whether another page exists.
</ResponseField>

<ResponseField name="nextCursor" type="string">
  Opaque next-page cursor. Present only when `hasMore` is `true`.
</ResponseField>

```json theme={null}
{
  "drafts": [
    {
      "id": "42",
      "text": "Just shipped dark mode. What feature should we build next?",
      "topic": "product update",
      "goal": "conversation",
      "createdAt": "2026-02-24T10:30:00.000Z",
      "updatedAt": "2026-02-24T10:30:00.000Z"
    },
    {
      "id": "38",
      "text": "Three lessons from building a real-time API.",
      "goal": "authority",
      "createdAt": "2026-02-23T16:15:00.000Z",
      "updatedAt": "2026-02-23T16:15:00.000Z"
    }
  ],
  "hasMore": true,
  "nextCursor": "MjAyNi0wMi0yM1QxNjoxNTowMC4wMDBafDM4"
}
```

### 400 Invalid cursor

```json theme={null}
{
  "error": "invalid_input",
  "message": "Cursor invalid. Use nextCursor from the previous page, or omit it to start over."
}
```

The `afterCursor` value is not a `nextCursor` from this route. Send the last
`nextCursor` you received, or omit `afterCursor` to start from the first page.

### 401 Unauthenticated

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

Authentication is missing or invalid. Replace the credential before retrying.

### 429 Rate limited

```json theme={null}
{
  "error": "rate_limit_exceeded",
  "message": "Too many requests. Try again later.",
  "retryAfter": 1
}
```

Too many requests reached the route. Wait for `Retry-After` before retrying.

<Note>
  **Related.** [Create Draft](/api-reference/drafts/create) to save tweet text, [Get Draft](/api-reference/drafts/get) to retrieve one record, or [Delete Draft](/api-reference/drafts/delete) to remove it.
</Note>


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