---
title: "Ingest images from URLs"
method: POST
path: "/api/v1/assets/from-url"
tags: ["Asset Management"]
---

# Ingest images from URLs

`POST /api/v1/assets/from-url`

## Request

- Base URL: `https://api.imagestep.dev`
- URL: `https://api.imagestep.dev/api/v1/assets/from-url`
- Auth: API key in header `Authorization`

Fetches each public image URL here, stores it and creates an asset — the same asset an
upload creates, in PROCESSING until ingest has written dimensions and metadata.

One result per URL, in request order: `{url, id, status, name}` or `{url, error}` with
the usual `code` / `retryable` / `param`. A private or unresolvable address, a URL that
is not an image (`unsupported_format`), a body over the limit or a host that fails for now
(`provider_unavailable`, retryable) fails that URL only. As on POST /api/v1/assets/upload,
the same bytes are the same asset: bytes the account already holds as a DONE asset (same
SHA-1) come back as that asset with `existing: true`, and so does a URL whose bytes an earlier
URL of the same request already brought in — nothing new is stored, and a duplicate keeps its
own name, collection and tags. So a URL that landed can be sent again; an Idempotency-Key still
replays the whole answer, the rows that failed included.

LIMITS: at most 20 URLs per call; each body at most 25 MB; redirects are not followed;
the asset count for all URLs is checked before anything is fetched. A response that declares a
type that is not an image (and a URL with no image extension), or a length over the limit, is
refused before its body is read. Each URL takes one of the synchronous endpoints' concurrency
slots while it is fetched and gives it back after: over your account's → `429 rate_limited`, the
node full → `503 provider_unavailable`; both retryable, with `Retry-After`. Refused at the first
URL, that is the request's answer; at a later one, it is that URL's row (`retryable: true`).

USE THIS WHEN:
- The image is somewhere on the internet and you want an `asset_id` for a job

DO NOT USE WHEN:
- You hold the bytes → `POST /api/v1/assets/upload` (one file, up to 25 MB), or
  `POST /api/v1/assets/stage-upload` → PUT → finish-upload for larger files and batches
- You only want one image transformed and back → `POST /api/v1/images/transform` with `{"url": …}`

## Headers

- `Idempotency-Key` string

## Request body

- FromUrlRequest: Images to ingest by URL
  - `urls` string[], required: Public http(s) image URLs, fetched by this service: at least one, at most 20.
  - `collection` string: Collection to put every new asset in (optional; at most 200 characters, and names starting with job: are reserved).
  - `tags` string[]: Tags attached to every new asset (optional): your own labels, matched exactly by GET /api/v1/assets?tag= (at most 50, each at most 100 characters)
  - `retentionDays` integer: Keep every new asset this many days instead of your plan's retention — shorter only; more is kept for the plan's time (imagestep#591). Each asset's `expiresAt` says what was stamped.

## Response `200`

One result per URL, in request order — a refused URL is a result with `error`, not a failed request; bytes you already hold are that asset, `existing: true`

## Other responses

- `400`: - `invalid_param` on `urls` — empty, or more than 20 URLs - `invalid_param` on `collection` or `tags` — over a limit, a control character, or a collection starting with `job:`
- `409`: Conflict — `error.code` is `idempotency_key_reuse` (the key already means a different request) or `request_in_progress` (the first request under this key has not settled yet; retryable).
- `422`: `asset_count_exceeded` on `urls` — more URLs than your plan's asset limit has room for; nothing is fetched
- `429`: Rate limited — `error.code` is `rate_limited` and `retryable` is true. `Retry-After` says how many seconds to wait.
- `503`: `provider_unavailable` — this node is at its concurrent-request capacity (`details.reason` is `capacity`). Retryable, after `Retry-After`

---

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