Create a link for a drop, a tip, a subscription or the profile
Creates one link id. The link is `https://www.dropfans.io/buy/{id}` (web) and `https://t.me/<bot>/app?startapp={id}` (Telegram), with no parameters. The drop, the tip amount and your `externalRef` are stored with the id.
/api/external/linksNewCreates one link id. The link is https://www.dropfans.io/buy/{id} (web) and https://t.me/<bot>/app?startapp={id} (Telegram), with no parameters. The drop, the tip amount and your externalRef are stored with the id.
buy: a drop (productIdfrom create-drop) opens its checkout. A subscriber-only drop opens the subscribe sheet instead.tip: opens the tip sheet, prefilled withamountUsdwhen you set one ($5–$750), or the fan picks.subscribe: opens the subscribe sheet.profileopens the profile.
externalRef (1–32 of A-Z a-z 0-9 _ -) comes back on the sale: the drop.paid / tip.paid webhooks and GET /orders and /tips, together with the link's linkId. One link per fan per drop is the usual pattern.
The same request always returns the same link, so it's safe to call again. With a key that belongs to an app, sales through the link earn your app owner commission for life, and the call makes your app the account's connected CRM.
Authentication
Send the creator's API key as a bearer token: Authorization: Bearer dpfn_…. See Authentication & API keys.
An agency's own key can call this for a creator it manages by adding X-Dropfans-Account: <account id>. See Agencies.
Request body
Content type: application/json
| Name | Type | Required | Description |
|---|---|---|---|
type | string | Required | What the link opens. One of: buy, tip, subscribe, profile |
productId | string | Optional | The drop, for buy (required there, refused otherwise). Must belong to the account. |
amountUsd | number | Optional | Fixed tip amount for tip ($5–$750). Omit to let the fan pick. |
externalRef | string | Optional | Your per-fan reference, returned on the sale. Max length: 32 Pattern: ^[A-Za-z0-9_-]{1,32}$ |
{
"type": "buy",
"productId": "cmv5tmpcm0015h4oucxkgc4ma",
"externalRef": "tg_48213377"
}{
"type": "tip",
"amountUsd": 20,
"externalRef": "tg_48213377"
}Responses
| Name | Type | Description |
|---|---|---|
id | string | The link id (10 of a-z 0-9). Comes back as linkId on sales through the link. |
type | string | What the link opens. One of: buy, tip, subscribe, profile |
url | string | The web link — /buy/{id}, no parameters.Format: uri |
telegram | string | null | The Telegram Mini App link (startapp={id}). Null when the shared Dropfans bot is not configured.Format: uri |
productId | string | null | The drop of a buy link. |
amountUsd | number | null | Fixed amount of a tip link; null = the fan picks. |
externalRef | string | null | Your per-fan reference, returned on the sale. |
createdAt | string | When the link was created. Format: date-time |
{
"id": "q7m2c8zk4w",
"type": "buy",
"url": "https://www.dropfans.io/buy/q7m2c8zk4w",
"telegram": "https://t.me/DropfansBot/app?startapp=q7m2c8zk4w",
"productId": "cmv5tmpcm0015h4oucxkgc4ma",
"amountUsd": null,
"externalRef": "tg_48213377",
"createdAt": "2026-10-29T12:00:00.000Z"
}Errors
| Status | Body | When |
|---|---|---|
| 400 | {"error":"type must be one of profile, tip, subscribe, buy","code":"invalid_type"} | Unknown `type`. Also `invalid_external_ref`, `invalid_amount` (out of range, or not a tip) and `invalid_product` (productId on a non-buy link). |
| 404 | {"error":"Drop not found","code":"product_not_found"} | `productId` is missing, unknown or another account’s. |
| 409 | {"error":"Set a username first","code":"username_required"} | The creator has no username yet. |
| 400 | {"error":"X-Dropfans-Account must be an account id (from GET /api/external/agency/creators), not a username.","code":"invalid_account_header"} | `X-Dropfans-Account` is not an account id. |
| 403 | {"error":"This key cannot act for that account.","code":"account_not_managed"} | `X-Dropfans-Account` names an account this key cannot act for: not an agency’s own key, no ACCEPTED management, or the agency or account is suspended, banned or deleted (an unknown id gets the same answer). |
Rate limiting
Every response carries the X-RateLimit-Tier header and, on limited tiers, the per-minute and per-day trios — read X-RateLimit-Remaining and X-RateLimit-Reset instead of hardcoding limits. Details in Rate limits.
Code samples
curl -X POST "https://www.dropfans.io/api/external/links" \
-H "Authorization: Bearer $DROPFANS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "buy",
"productId": "cmv5tmpcm0015h4oucxkgc4ma",
"externalRef": "tg_48213377"
}'const res = await fetch(`https://www.dropfans.io/api/external/links`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.DROPFANS_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"type": "buy",
"productId": "cmv5tmpcm0015h4oucxkgc4ma",
"externalRef": "tg_48213377"
}),
});
console.log(await res.json());import os
import requests
res = requests.post(
"https://www.dropfans.io/api/external/links",
headers={"Authorization": f"Bearer {os.environ['DROPFANS_API_KEY']}"},
json={
"type": "buy",
"productId": "cmv5tmpcm0015h4oucxkgc4ma",
"externalRef": "tg_48213377",
},
)
print(res.json())Questions? [email protected]
