Urlicer API v1

Bulk create

Create up to 10,000 links in one request, and read the per-row results.

Importing a spreadsheet, generating a link per customer, migrating from another shortener: all of it is one request rather than thousands. That matters for more than tidiness: the batch costs a single request against your per-minute budget, so an import that would take hours one link at a time takes seconds.

Create links in bulk

POST /api/v1/links/bulk

Up to 10,000 links in a single request, with a result row for each one.

Body fields
Field Type Required Description
links array required The links to create, at most 10,000. Each entry takes url, and optionally alias, expiresOn and password: the same fields as a single create.
domain string optional A custom domain of yours, applied to every link in the batch.
expiresOn integer optional A default expiry in unix seconds for the whole batch. An expiry on an individual link wins over it.
Request
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"},
    {"url": "nope"}
  ],
  "expiresOn": 1790000000
}'
Response
{
  "created": 2,
  "failed": 1,
  "results": [
    {
      "url": "nope",
      "short": null,
      "code": null,
      "success": false,
      "error": "Wrong URL format."
    },
    {
      "url": "https://example.com/one",
      "short": "https://ivi.to/Aipw",
      "code": "Aipw",
      "success": true,
      "error": null
    },
    {
      "url": "https://example.com/two",
      "short": "https://ivi.to/product-launch",
      "code": "product-launch",
      "success": true,
      "error": null
    }
  ]
}
Response fields
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.
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.
  • 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.

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. Failures that were caught before creation come back first, then the links that were created. 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

Three things 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 feature error naming it. One row asking for a gated feature stops the batch, so it is worth filtering those out before sending rather than discovering it at row 900.
  • 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 quota error and nothing is created. The batch is never half-applied to fit the remaining allowance.

Rows that simply fail validation, like a URL that is not a URL, are different: they never reach creation, so they cost no allowance and stop nothing. 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.