---
title: "Create many links to one destination"
method: POST
path: "/api/links/bulk-create"
tags: ["Links"]
---

# Create many links to one destination

`POST /api/links/bulk-create`

## Request

- Base URL: `https://www.flyn.to`
- URL: `https://www.flyn.to/api/links/bulk-create`
- Auth: HTTP bearer

Link multiplier: creates 2 to 50 short links to the same destination in one atomic insert, all carrying one batch tag. No link.create webhooks are sent for these links.

**Auth:** API key (`Bearer flyn_sk_live_...`), or the signed-in dashboard session cookie when no API key header is sent. MCP OAuth access tokens are not accepted here and get 401.

**Plan:** Pro, Lifetime or Team (a member of a paying owner's workspace has the effective plan `team`) only; Free gets 403 `UPGRADE_REQUIRED` before the body is read. The viewer team role gets 403.

**Rate limit:** 10 requests per 60 s per client IP, fixed window, in memory per server instance, checked before authentication.

## Request body

- BulkCreateRequest
  - `url` string, required: Shared destination. Same rules as POST /api/links (https:// prepended, mailto:, tel: and sms: accepted, ftp refused, host with a dot, not a Flyn host, public DNS). One safety scan covers the batch (422).
  - `quantity` integer, required: How many links. Floored first, so 2.9 becomes 2; numeric strings are accepted.
  - `tag` string: Batch tag. Trimmed and cut to 50 characters. Default `batch-<6 random characters>`.
  - `title` string: Shared title. Trimmed and cut to 200 characters. Default: the part of the URL after `//` up to the next `/` (usually the hostname, but it keeps any port or userinfo, and a query or fragment when there is no path), else "Untitled".
  - `domain` string: Default: the current Flyn short domain; the legacy value `www.flyn.to` is treated as a request for that default. Any other value must be a custom domain owned by the workspace, else 403.

## Response `201`

The batch was created.

- BulkCreateResponse
  - `created` integer, required
  - `tag` string, required
  - `url` string, required
  - `links` object[], required
    - `slug` string, required
    - `shortUrl` string, uri, required

## Other responses

- `400`: Bad JSON, missing url, quantity out of range, or URL validation. Messages: "Invalid JSON", "url is required", "quantity must be a whole number between 2 and 50", "Unsupported URL protocol", "Invalid URL", "Cannot shorten Flyn URLs", "Private/internal URLs are not allowed".
- `401`: No valid API key and no dashboard session. Messages: "Invalid or revoked API key", "API key has expired", "Unauthorized. Sign in to the dashboard, or pass a Bearer API key in the Authorization header.", or "Unauthorized" when the session lookup itself fails. An MCP OAuth token lands here too.
- `403`: Free plan (code `UPGRADE_REQUIRED`), viewer role, custom domain not owned, or a suspended account.
- `422`: The destination failed the safety scan.
- `429`: More than 10 requests in the window. Counted per client IP. Retry-After gives the seconds until the window resets.
- `500`: The insert failed.

---

[API](https://skmtc.dev/flyn/apis/flyn-api.md) · [All operations](https://skmtc.dev/flyn/apis/flyn-api/llms.txt) · [OpenAPI document](https://skmtc.dev/flyn/apis/flyn-api/revisions/977962a98ccb?raw)
