Bulk create
Create up to 10,000 links in one request, and read the per-row results.
If you are importing a spreadsheet or generating a link per customer, send the links in one request instead of one request each. The whole batch costs a single request against your per-minute budget, so an import that would take over an hour one link at a time takes seconds.
Create links in bulk
/api/v1/links/bulk
Up to 10,000 links in a single request, with a result row for each one.
| Field | Type | Required | Description |
|---|---|---|---|
links |
array | required | The links to create, at most 10,000. Each entry takes url, and optionally alias, expiresOn, password, title, notes, maxClicks, startsOn and fallbackUrl: the same fields as a single create. |
domain |
string | optional | A custom domain of yours, applied to every link in the batch. "default" uses the workspace's default domain; without the field the batch stays on ivi.to. |
expiresOn |
integer | optional | A default expiry in unix seconds for the whole batch. An expiry on an individual link wins over it. |
maxClicks |
integer | optional, Pro and above | A default click limit for the whole batch. A value on an individual link wins over it. |
startsOn |
integer | optional, Pro and above | A default start in unix seconds for the whole batch. A value on an individual link wins over it. |
fallbackUrl |
string | optional, Pro and above | A default fallback URL for the whole batch. A value on an individual link wins over it. A row whose limits are not valid fails on its own, with the reason. |
folder |
string | optional | A folder for the whole batch, by path: "Campaigns/Q4". Levels that do not exist are created, once, before the batch runs. There is no per-row folder: send one batch per folder, or move the rest afterwards with PATCH. |
curl -X POST "https://urlicer.com/api/v1/links/bulk" \
-H "Authorization: Bearer url_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"links": [
{"url": "https://example.com/one"},
{"url": "https://example.com/two", "alias": "product-launch", "title": "Product launch"},
{"url": "nope"}
],
"folder": "Campaigns/Q4",
"expiresOn": 1790000000
}'
{
"created": 2,
"failed": 1,
"results": [
{
"url": "nope",
"short": null,
"code": null,
"success": false,
"error": "Wrong URL format.",
"warning": null
},
{
"url": "https://example.com/one",
"short": "https://ivi.to/Aipw",
"code": "Aipw",
"success": true,
"error": null,
"warning": null
},
{
"url": "https://example.com/two",
"short": "https://ivi.to/product-launch",
"code": "product-launch",
"success": true,
"error": null,
"warning": null
}
]
}
| Field | Type | Description |
|---|---|---|
created |
integer | How many links were created. |
failed |
integer | How many rows did not produce a link. |
results |
array | One row per link you sent, described below. Not in the order you sent them. |
results[].url |
string | The destination from your request, which is how you match a row back to your input. On a created row it is the destination as saved: if your workspace's UTM naming rules lowercase tags, its utm_source, utm_medium and utm_campaign may differ in case from what you sent. |
results[].short |
string or null | The finished short link, or null if this row failed. |
results[].code |
string or null | The code alone, or null if this row failed. |
results[].success |
boolean | Whether this row produced a link. |
results[].error |
string or null | Why this row failed, in plain words. Null on success. |
results[].warning |
string or null | Set on a created row that broke your workspace's UTM naming rules while they warn, saying what. Null otherwise. |
- Bulk creation is a paid feature. On a plan without it the whole request is refused with a feature error and nothing is created.
- The entire batch counts as one request against your per-minute rate limit, however many links it carries.
- A batch folder is created before any link is, so a folder path that cannot be made, such as one deeper than five levels, refuses the whole request and creates nothing. The result rows do not repeat the folder: every created link is in it.
- Your workspace's UTM naming rules are applied to each link on its own. When they block, a link with a value that is not on a list fails with the reason in "error" and the rest of the batch is still created.
Some rows can fail while others succeed
This is the one endpoint that does not answer all-or-nothing. A batch with a malformed URL in the middle of it still creates every other link, and tells you which one it dropped and why. That is deliberate: a thousand-row import should not be thrown away because row 400 has a typo in it.
So a 200 here does not mean everything worked. Read
failed, and if it is not zero, read the rows.
The result rows are not in the order you sent them. Rows that failed
validation come first. Next come the links that were created, and after them the rows
refused at creation, such as a destination that is not allowed or is already a short link,
or an alias that is taken. Match rows to your input by the url field, never
by position. If the same destination appears
twice in your batch, expect two rows carrying it.
What still fails the whole batch
These are checked before any link is created, and each of them refuses the entire request:
- A feature your plan does not include. If any row asks for a custom
alias, an expiry or a password, or the batch names a custom domain, and your plan does
not include that, the request is refused with a
featureerror naming it. One row asking for a gated feature stops the batch, so filter those rows out before sending. - A domain that is not yours, or not verified and active. The request
answers
400validation. - More than 10,000 links. Split the work and send it as several batches.
- Not enough monthly link allowance for the whole batch. You get a
quotaerror and nothing is created. The batch is never half-applied to fit the remaining allowance.
Rows that fail validation, such as a URL that is not a URL or an alias longer than 40 characters, are handled differently. They never reach creation, so they cost no allowance and do not stop the batch. They come back as failed rows.
Aliases in a batch
A row whose alias is already taken fails on its own, with the rest of the batch unaffected. Its error says so in words rather than as a code: the machine-readable alias_taken error belongs to the single-link create, which has just one alias to talk about.
If you are generating aliases yourself, generate them idempotently, from a record id say, so that re-running a failed import produces the same aliases rather than a second set of links pointing at the same places.