> ## Documentation Index
> Fetch the complete documentation index at: https://www.dropfans.io/developers/llms.txt
> Use this file to discover all available pages before exploring further.

# POST /api/external/posts — Publish a post to the For You feed

> Rate limit: 5 posts per creator per rolling 24 hours. Bursts are fine — only the daily volume is capped. Exceeding it returns 429 with a Retry-After header.

- Source: https://www.dropfans.io/developers/reference/create-post
- Section: API reference
- OpenAPI: https://www.dropfans.io/developers/openapi.json

Rate limit: 5 posts per creator per rolling 24 hours. Bursts are fine — only the daily volume is capped. Exceeding it returns 429 with a Retry-After header.

Three shapes:
  • TEXT   — caption only.
  • MEDIA  — a gallery of 1-10 images from your vault, each free or paid.
  • DROP   — attach an existing approved drop by productId.

For MEDIA, list your vault first (GET /api/external/vault) and use the id
of an item whose moderationStatus is APPROVED to publish without waiting.

Everything posted here goes through exactly the same moderation as a post written in the web composer: captions run the prohibited-word filter, and media posts stay PENDING until every image clears the NSFW pipeline. You cannot use this API to bypass review.

Account types: CREATOR, AGENCY only.

## Authentication

`Authorization: Bearer dpfn_...` — an API key generated in the creator's dashboard (Vault → API Connect). One key = one creator. Missing or invalid keys return 401 `{"error":"Unauthorized","code":"unauthorized"}`.

## Request body (application/json)

| Field | Type | Required | Description |
|---|---|---|---|
| `caption` | string |  | Post text. Required for kind=TEXT. Silently truncated to 2000 characters. |
| `kind` | `TEXT` \\| `MEDIA` \\| `SUBSCRIPTION` \\| `COMMUNITY` |  | Post shape. DROP is not a request value — send productId instead. |
| `productId` | string |  | Attach an existing approved drop. Forces kind=DROP. Must belong to you. |
| `media` | object[] |  | Required when kind=MEDIA. |
| `scheduledAt` | string |  | ISO-8601. At least 1 minute ahead, at most 30 days. Omit to publish now. |

Example — Text post:

```json
{
  "caption": "im 5 min away, wyd?"
}
```

Example — Free image from the vault:

```json
{
  "kind": "MEDIA",
  "caption": "rate my fit 1-10",
  "media": [
    {
      "vaultItemId": "clxv1a2b30001item",
      "isPaid": false,
      "order": 0
    }
  ]
}
```

Example — Free teaser + paid unlock, scheduled:

```json
{
  "kind": "MEDIA",
  "caption": "can i show you??",
  "scheduledAt": "2026-08-26T19:00:00Z",
  "media": [
    {
      "vaultItemId": "clxv1a2b30001item",
      "isPaid": false,
      "order": 0
    },
    {
      "vaultItemId": "clxv1a2b30002item",
      "isPaid": true,
      "price": 10,
      "order": 1
    }
  ]
}
```

## Responses

### 201

Post created. Check `status`: APPROVED means it is already live, PENDING means it is still in review and will go live by itself once it clears.

| Field | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes | The new post id — poll GET /api/external/posts/{id} with it. |
| `status` | `PENDING` \\| `APPROVED` | yes | PENDING is normal, not an error: media posts wait on the NSFW pipeline and a flagged caption waits on a human. |
| `pending` | boolean | yes | Convenience mirror of status === "PENDING". |
| `scheduledAt` | string \\| null |  | Echoed schedule time, or null for immediate posts. |
| `url` | string \\| null |  | The creator’s profile URL (where the post appears once live). Null when the creator has no username yet. |

```json
{
  "id": "clxp0st000001feed",
  "status": "PENDING",
  "pending": true,
  "scheduledAt": null,
  "url": "https://www.dropfans.io/u/valeria"
}
```

## Errors

| Status | Body | When |
|---|---|---|
| 400 | `{"error":"Invalid JSON body"}` | The body is not valid JSON. |
| 400 | `{"error":"media must be a non-empty array for kind=MEDIA"}` | kind=MEDIA without media. |
| 400 | `{"error":"A post can carry at most 10 media items"}` | More than 10 media entries. |
| 400 | `{"error":"media[0].vaultItemId is required"}` | A media entry lacks vaultItemId (index varies). |
| 400 | `{"error":"media[0].price must be at least $5 for a paid item"}` | isPaid without a valid price (index varies). |
| 400 | `{"error":"Invalid schedule time"}` | scheduledAt is not a parseable date. |
| 400 | `{"error":"Schedule time must be at least a minute in the future"}` | scheduledAt is in the past or under a minute ahead. |
| 400 | `{"error":"Posts can be scheduled at most 30 days ahead"}` | scheduledAt beyond 30 days. |
| 400 | `{"error":"Write something first"}` | kind=TEXT with an empty caption. |
| 400 | `{"error":"Drop is not approved yet"}` | productId points at a drop still in moderation — check GET /api/external/drops/{id}. |
| 401 | `{"error":"Unauthorized","code":"unauthorized"}` | Missing or invalid API key. |
| 403 | `{"error":"Only creators can post"}` | The key belongs to a CONSUMER account. |
| 404 | `{"error":"Drop not found"}` | productId does not exist on this account. |
| 422 | `{"error":"Your post contains a word that isn’t allowed.","matchedWord":"…"}` | Caption tripped the prohibited-word filter — rewrite it and retry. |
| 429 | `{"error":"You’ve reached the posting limit (5 per day). Try again in …"}` | Daily posting cap (5 per rolling 24h) reached. Retry-After header = seconds to wait. This is separate from the API rate limit (whose body carries code:"rate_limited"). |

## Notes

The 429 here (posting cap, prose body with Retry-After) is a different limit from the gateway rate limit (429 with `code:"rate_limited"`) — handle both. SUBSCRIPTION and COMMUNITY kinds additionally require an active subscription setup / VIP Telegram channel on the account and 400 otherwise.

## Rate limiting

Per-key fixed windows by tier (Personal 60/min · 5,000/day; approved apps 300/min · 50,000/day; Dropfans-operated integrations exempt). Read the live values from X-RateLimit-Tier, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset and their -Day variants; a 429 carries Retry-After and `{"error":"Rate limit exceeded","code":"rate_limited"}`. See [Rate limits](https://www.dropfans.io/developers/concepts/rate-limits.md).

## Code samples

### curl

```bash
curl -X POST "https://www.dropfans.io/api/external/posts" \
  -H "Authorization: Bearer $DROPFANS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "caption": "im 5 min away, wyd?"
}'
```

### Node

```javascript
const res = await fetch(`https://www.dropfans.io/api/external/posts`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.DROPFANS_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "caption": "im 5 min away, wyd?"
  }),
});
console.log(await res.json());
```

### Python

```python
import os
import requests

res = requests.post(
    "https://www.dropfans.io/api/external/posts",
    headers={"Authorization": f"Bearer {os.environ['DROPFANS_API_KEY']}"},
    json={
        "caption": "im 5 min away, wyd?",
    },
)
print(res.json())
```

## OpenAPI

```json
{
  "method": "POST",
  "path": "/api/external/posts",
  "operationId": "createPost",
  "tags": [
    "posts"
  ],
  "summary": "Publish a post to the For You feed",
  "description": "Rate limit: 5 posts per creator per rolling 24 hours. Bursts are fine — only the daily volume is capped. Exceeding it returns 429 with a Retry-After header.\n\nThree shapes:\n  • TEXT   — caption only.\n  • MEDIA  — a gallery of 1-10 images from your vault, each free or paid.\n  • DROP   — attach an existing approved drop by productId.\n\nFor MEDIA, list your vault first (GET /api/external/vault) and use the id\nof an item whose moderationStatus is APPROVED to publish without waiting.\n\nEverything posted here goes through exactly the same moderation as a post written in the web composer: captions run the prohibited-word filter, and media posts stay PENDING until every image clears the NSFW pipeline. You cannot use this API to bypass review.",
  "requestBody": {
    "required": true,
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "properties": {
            "caption": {
              "type": "string",
              "description": "Post text. Required for kind=TEXT. Silently truncated to 2000 characters.",
              "example": "im 5 min away, wyd?",
              "maxLength": 2000
            },
            "kind": {
              "type": "string",
              "description": "Post shape. DROP is not a request value — send productId instead.",
              "enum": [
                "TEXT",
                "MEDIA",
                "SUBSCRIPTION",
                "COMMUNITY"
              ],
              "default": "TEXT"
            },
            "productId": {
              "type": "string",
              "description": "Attach an existing approved drop. Forces kind=DROP. Must belong to you."
            },
            "media": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "vaultItemId": {
                    "type": "string",
                    "description": "A vault item you own (GET /api/external/vault)."
                  },
                  "isPaid": {
                    "type": "boolean",
                    "description": "Paid items are shown blurred and mint a PPV unlock. Note: posting via API bakes no custom blur preview — paid items fall back to a gaussian blur.",
                    "default": false
                  },
                  "price": {
                    "type": "number",
                    "description": "USD. Required when isPaid is true. Minimum $5.",
                    "minimum": 5
                  },
                  "order": {
                    "type": "integer",
                    "description": "Display order. Defaults to array index."
                  }
                },
                "required": [
                  "vaultItemId"
                ],
                "description": "One gallery entry."
              },
              "description": "Required when kind=MEDIA.",
              "maxItems": 10
            },
            "scheduledAt": {
              "type": "string",
              "description": "ISO-8601. At least 1 minute ahead, at most 30 days. Omit to publish now.",
              "format": "date-time"
            }
          }
        },
        "examples": {
          "text": {
            "summary": "Text post",
            "value": {
              "caption": "im 5 min away, wyd?"
            }
          },
          "mediaFree": {
            "summary": "Free image from the vault",
            "value": {
              "kind": "MEDIA",
              "caption": "rate my fit 1-10",
              "media": [
                {
                  "vaultItemId": "clxv1a2b30001item",
                  "isPaid": false,
                  "order": 0
                }
              ]
            }
          },
          "mediaPaid": {
            "summary": "Free teaser + paid unlock, scheduled",
            "value": {
              "kind": "MEDIA",
              "caption": "can i show you??",
              "scheduledAt": "2026-08-26T19:00:00Z",
              "media": [
                {
                  "vaultItemId": "clxv1a2b30001item",
                  "isPaid": false,
                  "order": 0
                },
                {
                  "vaultItemId": "clxv1a2b30002item",
                  "isPaid": true,
                  "price": 10,
                  "order": 1
                }
              ]
            }
          }
        }
      }
    }
  },
  "responses": {
    "201": {
      "description": "Post created. Check `status`: APPROVED means it is already live, PENDING means it is still in review and will go live by itself once it clears.",
      "headers": {
        "X-RateLimit-Tier": {
          "description": "Rate-limit tier of the key: personal, app or first_party (first_party is unlimited).",
          "schema": {
            "type": "string",
            "enum": [
              "personal",
              "app",
              "first_party"
            ]
          }
        },
        "X-RateLimit-Limit": {
          "description": "Requests allowed per minute for this key (absent on first_party).",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Remaining": {
          "description": "Requests left in the current minute window.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Reset": {
          "description": "Epoch seconds when the minute window resets.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Limit-Day": {
          "description": "Requests allowed per UTC day for this key (absent on first_party).",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Remaining-Day": {
          "description": "Requests left in the current UTC day window.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Reset-Day": {
          "description": "Epoch seconds when the day window resets.",
          "schema": {
            "type": "integer"
          }
        }
      },
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "The new post id — poll GET /api/external/posts/{id} with it.",
                "example": "clxp0st000001feed"
              },
              "status": {
                "type": "string",
                "description": "PENDING is normal, not an error: media posts wait on the NSFW pipeline and a flagged caption waits on a human.",
                "enum": [
                  "PENDING",
                  "APPROVED"
                ]
              },
              "pending": {
                "type": "boolean",
                "description": "Convenience mirror of status === \"PENDING\"."
              },
              "scheduledAt": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Echoed schedule time, or null for immediate posts.",
                "format": "date-time"
              },
              "url": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The creator’s profile URL (where the post appears once live). Null when the creator has no username yet.",
                "format": "uri"
              }
            },
            "required": [
              "id",
              "status",
              "pending"
            ]
          },
          "example": {
            "id": "clxp0st000001feed",
            "status": "PENDING",
            "pending": true,
            "scheduledAt": null,
            "url": "https://www.dropfans.io/u/valeria"
          }
        }
      }
    },
    "400": {
      "description": "The body is not valid JSON. Also: kind=MEDIA without media. Also: More than 10 media entries. Also: A media entry lacks vaultItemId (index varies). Also: isPaid without a valid price (index varies). Also: scheduledAt is not a parseable date. Also: scheduledAt is in the past or under a minute ahead. Also: scheduledAt beyond 30 days. Also: kind=TEXT with an empty caption. Also: productId points at a drop still in moderation — check GET /api/external/drops/{id}.",
      "headers": {
        "X-RateLimit-Tier": {
          "description": "Rate-limit tier of the key: personal, app or first_party (first_party is unlimited).",
          "schema": {
            "type": "string",
            "enum": [
              "personal",
              "app",
              "first_party"
            ]
          }
        },
        "X-RateLimit-Limit": {
          "description": "Requests allowed per minute for this key (absent on first_party).",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Remaining": {
          "description": "Requests left in the current minute window.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Reset": {
          "description": "Epoch seconds when the minute window resets.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Limit-Day": {
          "description": "Requests allowed per UTC day for this key (absent on first_party).",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Remaining-Day": {
          "description": "Requests left in the current UTC day window.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Reset-Day": {
          "description": "Epoch seconds when the day window resets.",
          "schema": {
            "type": "integer"
          }
        }
      },
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "error": {
                "type": "string",
                "description": "Human-readable message describing what went wrong.",
                "example": "Vault item not found"
              }
            },
            "required": [
              "error"
            ]
          },
          "example": {
            "error": "Invalid JSON body"
          }
        }
      }
    },
    "401": {
      "description": "Missing or invalid API key.",
      "headers": {
        "X-RateLimit-Tier": {
          "description": "Rate-limit tier of the key: personal, app or first_party (first_party is unlimited).",
          "schema": {
            "type": "string",
            "enum": [
              "personal",
              "app",
              "first_party"
            ]
          }
        },
        "X-RateLimit-Limit": {
          "description": "Requests allowed per minute for this key (absent on first_party).",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Remaining": {
          "description": "Requests left in the current minute window.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Reset": {
          "description": "Epoch seconds when the minute window resets.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Limit-Day": {
          "description": "Requests allowed per UTC day for this key (absent on first_party).",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Remaining-Day": {
          "description": "Requests left in the current UTC day window.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Reset-Day": {
          "description": "Epoch seconds when the day window resets.",
          "schema": {
            "type": "integer"
          }
        }
      },
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "error": {
                "type": "string",
                "description": "Human-readable message.",
                "example": "Rate limit exceeded"
              },
              "code": {
                "type": "string",
                "description": "Machine-readable code: unauthorized, app_suspended, first_party_only, rate_limited, username_required.",
                "example": "rate_limited"
              }
            },
            "required": [
              "error",
              "code"
            ]
          },
          "example": {
            "error": "Unauthorized",
            "code": "unauthorized"
          }
        }
      }
    },
    "403": {
      "description": "The key belongs to a CONSUMER account.",
      "headers": {
        "X-RateLimit-Tier": {
          "description": "Rate-limit tier of the key: personal, app or first_party (first_party is unlimited).",
          "schema": {
            "type": "string",
            "enum": [
              "personal",
              "app",
              "first_party"
            ]
          }
        },
        "X-RateLimit-Limit": {
          "description": "Requests allowed per minute for this key (absent on first_party).",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Remaining": {
          "description": "Requests left in the current minute window.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Reset": {
          "description": "Epoch seconds when the minute window resets.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Limit-Day": {
          "description": "Requests allowed per UTC day for this key (absent on first_party).",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Remaining-Day": {
          "description": "Requests left in the current UTC day window.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Reset-Day": {
          "description": "Epoch seconds when the day window resets.",
          "schema": {
            "type": "integer"
          }
        }
      },
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "error": {
                "type": "string",
                "description": "Human-readable message describing what went wrong.",
                "example": "Vault item not found"
              }
            },
            "required": [
              "error"
            ]
          },
          "example": {
            "error": "Only creators can post"
          }
        }
      }
    },
    "404": {
      "description": "productId does not exist on this account.",
      "headers": {
        "X-RateLimit-Tier": {
          "description": "Rate-limit tier of the key: personal, app or first_party (first_party is unlimited).",
          "schema": {
            "type": "string",
            "enum": [
              "personal",
              "app",
              "first_party"
            ]
          }
        },
        "X-RateLimit-Limit": {
          "description": "Requests allowed per minute for this key (absent on first_party).",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Remaining": {
          "description": "Requests left in the current minute window.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Reset": {
          "description": "Epoch seconds when the minute window resets.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Limit-Day": {
          "description": "Requests allowed per UTC day for this key (absent on first_party).",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Remaining-Day": {
          "description": "Requests left in the current UTC day window.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Reset-Day": {
          "description": "Epoch seconds when the day window resets.",
          "schema": {
            "type": "integer"
          }
        }
      },
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "error": {
                "type": "string",
                "description": "Human-readable message describing what went wrong.",
                "example": "Vault item not found"
              }
            },
            "required": [
              "error"
            ]
          },
          "example": {
            "error": "Drop not found"
          }
        }
      }
    },
    "422": {
      "description": "Caption tripped the prohibited-word filter — rewrite it and retry.",
      "headers": {
        "X-RateLimit-Tier": {
          "description": "Rate-limit tier of the key: personal, app or first_party (first_party is unlimited).",
          "schema": {
            "type": "string",
            "enum": [
              "personal",
              "app",
              "first_party"
            ]
          }
        },
        "X-RateLimit-Limit": {
          "description": "Requests allowed per minute for this key (absent on first_party).",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Remaining": {
          "description": "Requests left in the current minute window.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Reset": {
          "description": "Epoch seconds when the minute window resets.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Limit-Day": {
          "description": "Requests allowed per UTC day for this key (absent on first_party).",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Remaining-Day": {
          "description": "Requests left in the current UTC day window.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Reset-Day": {
          "description": "Epoch seconds when the day window resets.",
          "schema": {
            "type": "integer"
          }
        }
      },
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "error": {
                "type": "string",
                "description": "Human-readable message.",
                "example": "Your post contains a word that isn’t allowed."
              },
              "matchedWord": {
                "type": "string",
                "description": "The prohibited word that tripped the filter — rewrite and retry."
              }
            },
            "required": [
              "error",
              "matchedWord"
            ]
          },
          "example": {
            "error": "Your post contains a word that isn’t allowed.",
            "matchedWord": "…"
          }
        }
      }
    },
    "429": {
      "description": "Daily posting cap (5 per rolling 24h) reached. Retry-After header = seconds to wait. This is separate from the API rate limit (whose body carries code:\"rate_limited\").",
      "headers": {
        "X-RateLimit-Tier": {
          "description": "Rate-limit tier of the key: personal, app or first_party (first_party is unlimited).",
          "schema": {
            "type": "string",
            "enum": [
              "personal",
              "app",
              "first_party"
            ]
          }
        },
        "X-RateLimit-Limit": {
          "description": "Requests allowed per minute for this key (absent on first_party).",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Remaining": {
          "description": "Requests left in the current minute window.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Reset": {
          "description": "Epoch seconds when the minute window resets.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Limit-Day": {
          "description": "Requests allowed per UTC day for this key (absent on first_party).",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Remaining-Day": {
          "description": "Requests left in the current UTC day window.",
          "schema": {
            "type": "integer"
          }
        },
        "X-RateLimit-Reset-Day": {
          "description": "Epoch seconds when the day window resets.",
          "schema": {
            "type": "integer"
          }
        },
        "Retry-After": {
          "description": "Seconds to wait before retrying (sent with 429s).",
          "schema": {
            "type": "integer"
          }
        }
      },
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "error": {
                "type": "string",
                "description": "Human-readable message describing what went wrong.",
                "example": "Vault item not found"
              }
            },
            "required": [
              "error"
            ]
          },
          "example": {
            "error": "You’ve reached the posting limit (5 per day). Try again in …"
          }
        }
      }
    }
  },
  "x-codeSamples": [
    {
      "lang": "cURL",
      "label": "curl",
      "source": "curl -X POST \"https://www.dropfans.io/api/external/posts\" \\\n  -H \"Authorization: Bearer $DROPFANS_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"caption\": \"im 5 min away, wyd?\"\n}'"
    },
    {
      "lang": "JavaScript",
      "label": "Node",
      "source": "const res = await fetch(`https://www.dropfans.io/api/external/posts`, {\n  method: 'POST',\n  headers: {\n    Authorization: `Bearer ${process.env.DROPFANS_API_KEY}`,\n    'Content-Type': 'application/json',\n  },\n  body: JSON.stringify({\n    \"caption\": \"im 5 min away, wyd?\"\n  }),\n});\nconsole.log(await res.json());"
    },
    {
      "lang": "Python",
      "source": "import os\nimport requests\n\nres = requests.post(\n    \"https://www.dropfans.io/api/external/posts\",\n    headers={\"Authorization\": f\"Bearer {os.environ['DROPFANS_API_KEY']}\"},\n    json={\n        \"caption\": \"im 5 min away, wyd?\",\n    },\n)\nprint(res.json())"
    }
  ],
  "x-dropfans-docs": "https://www.dropfans.io/developers/reference/create-post"
}
```

---

Previous: [Check which drops sold (batch)](https://www.dropfans.io/developers/reference/check-drop-status.md) · Next: [List your posts and their moderation status](https://www.dropfans.io/developers/reference/list-posts.md) · All pages: [llms.txt](https://www.dropfans.io/developers/llms.txt)
