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

# Save tweet writing samples for a reusable X style

> Save 1-100 approved tweet examples as a reusable writing profile under its lowercase path ID. Saving the same path ID replaces every example. The call is free.

<Panel>
  <Tabs defaultTabIndex={0} sync={false}>
    <Tab title="200" id="response-styles-save-200">
      ```json theme={null}
      {
        "xUsername": "professional voice",
        "tweetCount": 1,
        "isOwnAccount": false,
        "fetchedAt": "2026-08-02T18:30:00.000Z",
        "tweets": [
          {
            "id": "0",
            "text": "Excited to share our latest research.",
            "authorUsername": "professional voice",
            "createdAt": "2026-08-02T18:30:00.000Z"
          }
        ]
      }
      ```
    </Tab>

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

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

    <Tab title="429" id="response-styles-save-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>

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

Save approved Tweet text as a reusable, account-scoped writing profile.
Xquik stores each example as written. It generates no style labels.

The endpoint stores 1-100 Tweet objects under the lowercase path `{id}`. It trims
outer whitespace from every example. Internal punctuation, casing, links, and
line breaks remain available for later review.

It does not infer tone, vocabulary, sentiment, readability, or engagement.
It also does not fetch likes, replies, reposts, quotes, views, or followers.

## Save approved tweet writing samples

Use this endpoint when you already have reviewed examples. Those examples can
come from approved drafts, published Tweets, or internal writing guidelines.

| Tweet style profile column | Request or response source | Reuse rule |
| - | - | - |
| Profile key | Path `{id}` | Reuse the returned lowercase key. |
| Source tweets | Request `tweets[].text` | Review every supplied post before saving. |
| Sample ID | Response `tweets[].id` | Treat it as a local array position. |
| Sample author | Response `tweets[].authorUsername` | Expect the normalized label. |
| Sample timestamp | Response `tweets[].createdAt` | Record the style save time. |
| Profile timestamp | Response `fetchedAt` | Detect later replacements. |
| Ownership flag | Response `isOwnAccount` | Expect `false` for custom samples. |

These local sample IDs are not X Tweet IDs. Do not send them to Tweet lookup
or Tweet analytics endpoints.

## Choose representative tweet examples

Save posts that
show the patterns you want future drafts to follow.

| Writing feature | Include when relevant | Review before saving |
| - | - | - |
| Opening structure | Questions, statements, or short hooks | Remove accidental clickbait |
| Sentence length | Short posts and longer explanations | Keep the intended pacing |
| Formatting | Paragraphs, lists, or single lines | Keep line breaks that carry meaning |
| Punctuation | Colons, parentheses, or exclamation marks | Remove unapproved habits |
| Calls to action | Replies, links, or product prompts | Keep claims accurate |
| X vocabulary | Product names and audience terms | Remove internal jargon |
| Emoji and hashtags | Only approved usage | Avoid one-off campaign tags |
| Replies | Useful support or community responses | Exclude private customer details |

Do not treat every published Tweet as a good example. Remove crisis posts,
temporary promotions, outdated claims, and accidental wording.

The endpoint stores text exactly after trimming outer whitespace. It does not
decide whether an example fits your Twitter writing style.

## Name the profile in the path

The path `{id}` names the stored profile. Xquik trims it, drops a leading `@`
and converts it to lowercase before saving.

The body `label` is optional. When you send it, it must match `{id}`, ignoring
case and a leading `@`; the PUT route rejects a mismatch.

```text theme={null}
PUT /api/v1/styles/product-updates
body.label = "product-updates"
response.xUsername = "product-updates"
```

Spaces are valid inside `label`. Encode them when using the same path value.
A simple hyphenated label avoids confusing URLs.

## Replace an existing tweet style

Profiles belong to the authenticated Xquik account. The normalized path `{id}`
forms the account-scoped storage key.

Sending the same label replaces the entire saved Tweet array. It also resets
every local sample ID and save timestamp.

Send the complete approved set during every replacement. Omitting an old
example removes it from the saved profile.

A different path `{id}` creates or replaces another normalized key. Delete the
old key separately.

## Reuse saved tweet examples

Use the returned `xUsername` value as the profile key. The name remains
`xUsername` for both analyzed accounts and custom labels.

| Next operation | Key to send | Purpose |
| - | - | - |
| [Get Style](/api-reference/styles/get) | Path `{id}` | Retrieve all saved examples |
| [List Styles](/api-reference/styles/list) | None | Review available labels and counts |
| [Compare Styles](/api-reference/styles/compare) | `username1` or `username2` | Load 2 sample sets side by side |
| [Build a Post Draft](/api-reference/compose/create) | `styleUsername` | Return matched examples as `styleTweets` |
| [Delete Style](/api-reference/styles/delete) | Path `{id}` | Remove the profile |

Custom samples contain local sequential IDs, so they cannot provide
live likes, replies, reposts, quotes, bookmarks, or view counts.

## Request examples

<CodeGroup>
  ```bash cURL theme={null}
  curl -X PUT https://xquik.com/api/v1/styles/product-updates \
    -H "x-api-key: xq_YOUR_KEY_HERE" \
    -H "Content-Type: application/json" \
    -d '{
      "label": "product-updates",
      "tweets": [
        {"text": "Shipped faster follower exports today. CSV files now preserve profile IDs."},
        {"text": "New: filter Tweet replies before sending results to your webhook."},
        {"text": "Building an X monitor? Start with one profile and verify every event."}
      ]
    }' | jq
  ```

  ```javascript Node.js theme={null}
  const response = await fetch("https://xquik.com/api/v1/styles/product-updates", {
    method: "PUT",
    headers: {
      "x-api-key": "xq_YOUR_KEY_HERE",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      label: "product-updates",
      tweets: [
        {
          text: "Shipped faster follower exports today. CSV files now preserve profile IDs.",
        },
        {
          text: "New: filter Tweet replies before sending results to your webhook.",
        },
        {
          text: "Building an X monitor? Start with one profile and verify every event.",
        },
      ],
    }),
  });
  const result = await response.json();

  if (!response.ok) {
    throw new Error(`${response.status}: ${result.message}`);
  }

  console.log(result.xUsername, result.tweetCount);
  ```

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

  response = requests.put(
      "https://xquik.com/api/v1/styles/product-updates",
      headers={"x-api-key": "xq_YOUR_KEY_HERE"},
      json={
          "label": "product-updates",
          "tweets": [
              {
                  "text": (
                      "Shipped faster follower exports today. "
                      "CSV files now preserve profile IDs."
                  )
              },
              {
                  "text": (
                      "New: filter Tweet replies before sending results "
                      "to your webhook."
                  )
              },
              {
                  "text": (
                      "Building an X monitor? Start with one profile "
                      "and verify every event."
                  )
              },
          ],
      },
      timeout=30,
  )
  response.raise_for_status()
  result = response.json()
  print(result["xUsername"], result["tweetCount"])
  ```

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

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

  func main() {
      body, err := json.Marshal(map[string]interface{}{
          "label": "product-updates",
          "tweets": []map[string]string{
              {"text": "Shipped faster follower exports today. CSV files now preserve profile IDs."},
              {"text": "New: filter Tweet replies before sending results to your webhook."},
              {"text": "Building an X monitor? Start with one profile and verify every event."},
          },
      })
      if err != nil {
          panic(err)
      }

      request, err := http.NewRequest(
          http.MethodPut,
          "https://xquik.com/api/v1/styles/product-updates",
          bytes.NewReader(body),
      )
      if err != nil {
          panic(err)
      }
      request.Header.Set("x-api-key", "xq_YOUR_KEY_HERE")
      request.Header.Set("Content-Type", "application/json")

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

      var result map[string]interface{}
      if err := json.NewDecoder(response.Body).Decode(&result); err != nil {
          panic(err)
      }
      if response.StatusCode != http.StatusOK {
          panic(fmt.Sprintf("style save failed: %v", result))
      }
      fmt.Println(result["xUsername"], result["tweetCount"])
  }
  ```
</CodeGroup>

## Path parameters

<ParamField path="id" type="string" required>
  Route identifier. Keep it equal to the body `label`. The body label controls
  the stored profile key for PUT requests.
</ParamField>

## Headers

Send `x-api-key` with an Xquik API key. OAuth clients can send a bearer token
through the `Authorization` header instead.

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

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

## Body

<ParamField body="label" type="string">
  Optional profile label. When sent, it must match the path `{id}`, ignoring
  case and a leading `@`. It contains 1-50 characters and must start with a
  letter, number, or underscore. It accepts letters, numbers, spaces, dots, hyphens,
  apostrophes, and underscores. Xquik trims and lowercases this value.
</ParamField>

<ParamField body="tweets" type="object[]" required>
  Complete array of 1-100 approved Tweet examples.

  <Expandable title="Tweet object">
    <ParamField body="text" type="string" required>
      Non-empty Tweet text. Xquik trims outer whitespace before storage.
    </ParamField>
  </Expandable>
</ParamField>

## Response

### 200 OK

Returns the complete saved profile after insertion or replacement.

<ResponseField name="xUsername" type="string">
  Normalized lowercase value from the body `label`.
</ResponseField>

<ResponseField name="tweetCount" type="number">
  Number of saved examples. Matches the submitted array length.
</ResponseField>

<ResponseField name="isOwnAccount" type="boolean">
  Always `false` for a custom profile created from supplied text.
</ResponseField>

<ResponseField name="fetchedAt" type="string">
  ISO 8601 save timestamp shared by every returned sample.
</ResponseField>

<ResponseField name="tweets" type="object[]">
  Complete saved Tweet sample array.

  <Expandable title="Saved tweet sample">
    <ResponseField name="id" type="string">
      Local zero-based array position. This is not an X Tweet ID.
    </ResponseField>

    <ResponseField name="text" type="string">
      Submitted Tweet text after trimming outer whitespace.
    </ResponseField>

    <ResponseField name="authorUsername" type="string">
      Normalized custom profile label, not an X account lookup.
    </ResponseField>

    <ResponseField name="createdAt" type="string">
      ISO 8601 save timestamp, not an X publication time.
    </ResponseField>
  </Expandable>
</ResponseField>

```json theme={null}
{
  "xUsername": "product-updates",
  "tweetCount": 3,
  "isOwnAccount": false,
  "fetchedAt": "2026-08-02T18:30:00.000Z",
  "tweets": [
    {
      "id": "0",
      "text": "Shipped faster follower exports today. CSV files now preserve profile IDs.",
      "authorUsername": "product-updates",
      "createdAt": "2026-08-02T18:30:00.000Z"
    },
    {
      "id": "1",
      "text": "New: filter Tweet replies before sending results to your webhook.",
      "authorUsername": "product-updates",
      "createdAt": "2026-08-02T18:30:00.000Z"
    },
    {
      "id": "2",
      "text": "Building an X monitor? Start with one profile and verify every event.",
      "authorUsername": "product-updates",
      "createdAt": "2026-08-02T18:30:00.000Z"
    }
  ]
}
```

## Handle save-style errors

| Status | Error | Cause | Fix |
| - | - | - | - |
| `200` | None | Profile saved or replaced | Store `xUsername` for later requests. |
| `400` | `invalid_input` | Label or Tweet array failed validation | Correct the rejected field. |
| `401` | `unauthenticated` | Credential is missing or invalid | Replace the API key or bearer token. |
| `429` | `rate_limit_exceeded` | Request exceeded the rate limit | Wait for `Retry-After`, then retry once. |

The response widget lists every status in the OpenAPI contract.

### 400 Invalid input

A request returns `400` when any required input fails validation.

| Invalid input | Required correction |
| - | - |
| Missing or non-string `label` | Send one label string. |
| Blank `label` | Add at least 1 visible character. |
| Label longer than 50 characters | Shorten the label. |
| Unsupported label character | Use letters, numbers, spaces, dots, hyphens, apostrophes, or underscores. |
| Missing or non-array `tweets` | Send an array of Tweet objects. |
| Empty `tweets` array | Add at least 1 example. |
| More than 100 Tweet objects | Split or reduce the sample set. |
| Missing, non-string, or blank `text` | Send non-empty text for every object. |

Label validation can return a specific message. Other body failures use the
general invalid-input response.

```json theme={null}
{
  "error": "invalid_input",
  "message": "Label can only contain letters, numbers, spaces, dots, hyphens, and apostrophes."
}
```

The validator also accepts underscores. Its current message omits that
character.

### 401 Unauthenticated

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

Replace the credential before retrying. Do not retry invalid credentials in a
loop.

### 429 Rate limited

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

Wait for the `Retry-After` header. Then retry the complete replacement body.

## Tweet style save checklist

* Review every Tweet example before sending it.
* Remove customer names, private replies, and temporary claims.
* Name the profile in the path `{id}`; `label` is optional.
* Send 1-100 objects with non-empty `text` strings.
* Store the returned lowercase `xUsername` key.
* Confirm `tweetCount` matches the intended sample count.
* Expect `isOwnAccount` to remain `false`.
* Treat returned sample IDs as local positions.
* Keep a copy of the full array before replacing an existing profile.
* Handle `400`, `401`, and `429`.

## Tweet writing style questions

### What does save style analyze?

Nothing. Save Style stores the supplied Tweet text and profile metadata. It
does not calculate tone, vocabulary, sentiment, or performance.

### How many tweet examples can I save?

Send between 1 and 100 Tweet objects. Prefer examples that show
repeatable wording, structure, replies, links, and calls to action.

### Can I update an existing Twitter writing style?

Yes. Send the same normalized body label with the complete replacement array.
The operation replaces every stored example under that account-scoped key.

### Does the path ID rename a tweet style?

No. The body label controls the stored key. Keep both values aligned, and
delete the old key after creating a new label.

### Can saved tweet samples provide engagement analytics?

No. Custom samples use local sequential IDs. Use cached live Tweet samples for
likes, replies, reposts, quotes, bookmarks, and view counts.

### Can another Xquik account read my saved style?

No. Retrieval uses both the authenticated user and normalized profile key.
Another Xquik account has a separate style namespace.

### How do I use the style in a post draft?

Pass the returned `xUsername` as `styleUsername` to [Build a Post Draft](/api-reference/compose/create).
The response can include the matched examples in `styleTweets`.

<Note>
  Compare [Analyze Style](/api-reference/styles/analyze) before saving custom
  examples. Analyze Style caches Tweets from one X username. Save Style stores
  only the text you supply.
</Note>


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