Raster

Endpoints

Every REST route in the Raster API, with parameters, responses, and examples.

Organization endpoints live under https://api.raster.app/organizations/:organizationId/...; GET /me and POST /libraries sit at the root. The :organizationId segment is your organization slug. Library-scoped endpoints add /libraries/:libraryId. Every request requires the Api-Version: 2026-07-08 header, and every request except POST /libraries also requires Authorization: Bearer <API_KEY> (see Authentication).

MethodPathAccessDescription
GET/meReadResolve your key's organization + library scope.
POST/librariesAnonymousCreate an organization + library + API key with no account.
GET/organizations/:orgId/librariesReadList libraries in an organization.
POST/organizations/:orgId/librariesWriteCreate a library in an organization.
PATCH/organizations/:orgId/libraries/:libraryIdWriteRename a library.
GET/organizations/:orgId/libraries/:libraryId/assetsReadList assets in a library.
GET/organizations/:orgId/libraries/:libraryId/assets/:assetIdReadGet one asset by id.
GET/organizations/:orgId/search/assetsReadSearch assets across libraries.
GET/organizations/:orgId/libraries/:libraryId/tagsReadList tags in a library.
POST/organizations/:orgId/libraries/:libraryId/assetsWriteUpload one or more assets.
DELETE/organizations/:orgId/libraries/:libraryId/assetsWriteMove assets to trash by id (soft delete).
POST/organizations/:orgId/libraries/:libraryId/assets/tagWriteApply tags to a batch of assets.
POST/organizations/:orgId/libraries/:libraryId/assets/untagWriteRemove tags from a batch of assets.
PATCH/organizations/:orgId/libraries/:libraryId/assets/:assetId/descriptionWriteReplace the description on a single asset.
PATCH/organizations/:orgId/libraries/:libraryId/assets/:assetId/approvalReadSet the approval state of a single asset (OAuth only).
POST/organizations/:orgId/libraries/:libraryId/assets/transferWriteMove a batch of assets to another library.
POST/organizations/:orgId/libraries/:libraryId/assets/:assetId/promoteWriteMake a variant the asset's default.

GET /me

Resolves the organizations and libraries your credential (an API key or an OAuth access token) can act on. Call this first when you hold only a credential: it returns the organizationId (and authorized library ids) that every other endpoint needs as a path segment. A key, or an OAuth token bound to one organization, resolves that single organization; a token granted your whole account resolves every organization you belong to under organizations. Send the Authorization: Bearer and Api-Version headers as on every authenticated request.

Returns

{ data: { organizationId, organizationName, plan, libraries, user } }:

  • organizationId: the organization your key belongs to; pass it to every org-scoped endpoint.
  • organizationName: the organization's display name, or null when it has none.
  • plan: the organization's plan (e.g. pro, hobby), or null when none is set.
  • libraries: one { library, access } entry per library your key can act on. access is "write" (read and write) or "read" (read-only). Use GET /organizations/:orgId/libraries for their names and counts.
  • user: { name } for an OAuth access token, naming the account the token acts as so a client can label its connection. name is null when the account records none. Absent for an API key, which belongs to an organization rather than a person (as in the example below).

Example

Terminal
curl --request GET \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Api-Version: 2026-07-08' \
  --url 'https://api.raster.app/me'

Response:

{
  "data": {
    "organizationId": "acme-co",
    "organizationName": "Acme Co",
    "plan": "pro",
    "libraries": [
      { "library": "marketing", "access": "write" },
      { "library": "product-shots", "access": "write" },
      { "library": "archive", "access": "read" }
    ]
  }
}

POST /libraries

Creates a new organization, with a starter library and an API key for it, for a caller who has no Raster account and no API key yet. A client (typically an AI agent) calls it with a person's email, gets back a ready-to-use key, and starts uploading right away. The new library is held for 30 days so the person can claim it: they open the emailed link to move the library and everything in it into a real Raster account. Anything left unclaimed when the window closes is removed.

The same flow is available over MCP as the create_organization tool. To create a library inside an existing organization, use POST /organizations/:orgId/libraries instead.

POST to https://api.raster.app/libraries. The endpoint is anonymous: it creates the key, so send the request with no Authorization header. Include the Api-Version header and read the { data } / { error } envelope as on every other request. The endpoint has a per-IP rate limit (429) and the per-email quota below.

Parameters

A JSON request body:

Prop

Type

Returns

{ data: { organizationId, libraryId, apiKey, claimUrl, expiresAt, emailSent } }:

  • organizationId and libraryId: identify the new library. Pass both to every later asset call, e.g. POST /organizations/:orgId/libraries/:libraryId/assets.
  • apiKey: the Bearer token for those calls. It is returned only once, so store it immediately.
  • claimUrl: the link the user opens to claim the library into a Raster account.
  • expiresAt: ISO 8601 timestamp for when the claim window closes.
  • emailSent: true if the claim link was emailed. When false, surface claimUrl to the user directly.

Example

Terminal
curl --request POST \
  --header 'Api-Version: 2026-07-08' \
  --header 'Content-Type: application/json' \
  --url 'https://api.raster.app/libraries' \
  --data '{"email":"<your-email>","name":"Sample library"}'

Response:

{
  "data": {
    "organizationId": "k3m9x7p2qar8zt",
    "libraryId": "b7n2w4j8x1c5dq",
    "apiKey": "pk_…",
    "claimUrl": "https://raster.app/claim/…",
    "expiresAt": "2026-07-03T00:00:00.000Z",
    "emailSent": true
  }
}

Use the returned apiKey as the Authorization: Bearer token and the organizationId and libraryId as the target for every later call: upload, list, search, tag, and the other organization-scoped endpoints.

GET /organizations/:orgId/libraries

Lists the libraries in an organization that your credential can reach.

Parameters

Prop

Type

Returns

{ data: Library[] }. See Library.

Example

Terminal
curl --request GET \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Api-Version: 2026-07-08' \
  --url 'https://api.raster.app/organizations/acme-co/libraries?page=1&pageSize=20'

POST /organizations/:orgId/libraries

Creates a new library inside an organization. The caller must be an API key with Write access or, over OAuth, an Owner of the organization. A read-only key cannot create libraries. A library created with an API key is added to that key's allowlist, so the same key can upload to it immediately. The new library is shown to your whole team in the Raster app. To start a new organization from scratch with no account, use the anonymous POST /libraries.

The same flow is available over MCP as the create_library tool.

Parameters

Prop

Type

Returns

201 Created with { data: Library }. See Library. A new library has zeroed counters and no tags. On a free (hobby) plan at the library cap, returns 400 LIBRARY_LIMIT_EXCEEDED. On an organization whose Pro plan ended while it still has more than one member, returns 403 PRO_PLAN_CANCELED. A read-only key returns 403 API_KEY_READ_ONLY. An OAuth access token whose account is not an Owner of the organization returns 403 FORBIDDEN.

Example

Terminal
curl --request POST \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Api-Version: 2026-07-08' \
  --header 'Content-Type: application/json' \
  --url 'https://api.raster.app/organizations/acme-co/libraries' \
  --data '{"name":"Marketing","slug":"marketing"}'

PATCH /organizations/:orgId/libraries/:libraryId

Renames a library your API key has access to. The new name appears everywhere the library is shown, including each team member's view. The URL slug stays the same.

The same flow is available over MCP as the rename_library tool.

Parameters

Prop

Type

Returns

{ data: Library }, the updated Library. A key not authorized for the library, or a missing library, returns 404.

Example

Terminal
curl --request PATCH \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Api-Version: 2026-07-08' \
  --header 'Content-Type: application/json' \
  --url 'https://api.raster.app/organizations/acme-co/libraries/marketing' \
  --data '{"name":"Marketing 2026"}'

GET /organizations/:orgId/libraries/:libraryId/assets/:assetId

Fetches a single asset by id, in the same Asset shape that GET /organizations/:orgId/libraries/:libraryId/assets returns. Use it to read one asset's url, tags, description, and dimensions without paging the list. The :assetId is the id returned by list, search, or upload.

Parameters

Prop

Type

Returns

{ data: Asset }. See Asset. Returns the same 404 whether the asset does not exist, belongs to a different library, or is trashed.

Example

Terminal
curl --request GET \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Api-Version: 2026-07-08' \
  --url 'https://api.raster.app/organizations/acme-co/libraries/marketing/assets/k3m9x7p2qar8zt'

GET /organizations/:orgId/libraries/:libraryId/assets

Lists assets in a library, paginated and optionally filtered by tag.

Parameters

Prop

Type

Returns

{ data: Asset[] }. See Asset. The unfiltered list returns one entry per original asset, each carrying its live variants in the variants field. A tag-filtered list returns every matching asset and matching variant as its own entry (a variant carries its parentId), so variants are not additionally nested there.

Example

Terminal
curl --request GET \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Api-Version: 2026-07-08' \
  --url 'https://api.raster.app/organizations/acme-co/libraries/barcelona/assets?page=1&pageSize=20&tags=landscape,nature'

GET /organizations/:orgId/search/assets

Searches assets across the libraries your credential can reach, returning relevance-ranked hits with optional highlighted snippets. The endpoint is organization-scoped: pass libraries to narrow the search to a subset of authorized libraries, or omit it to search every library the credential can reach.

Parameters

Prop

Type

Returns

{ data: SearchResult }. See SearchResult and SearchHit.

Example

Terminal
curl --request GET \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Api-Version: 2026-07-08' \
  --url 'https://api.raster.app/organizations/acme-co/search/assets?q=sunset&libraries=barcelona,lisbon&page=1&pageSize=10'

Asking for an unauthorized library fails the whole request with 404 API_KEY_NOT_AUTHORIZED_FOR_LIBRARY. The request is rejected before any search runs, and no partial results are returned. To search every library the credential can reach, omit libraries entirely.

GET /organizations/:orgId/libraries/:libraryId/tags

Lists the tags in a library, with their hit counts.

Parameters

Prop

Type

Returns

{ data: Tag[] }. See Tag.

Example

Terminal
curl --request GET \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Api-Version: 2026-07-08' \
  --url 'https://api.raster.app/organizations/acme-co/libraries/barcelona/tags?limit=100'

POST /organizations/:orgId/libraries/:libraryId/assets

Uploads one or more files to a library. The request body is multipart/form-data with a repeatable files field.

Parameters

Prop

Type

Returns

{ data: { responseText: string, assets: Asset[] } }. Each entry has the same Asset shape that GET /assets returns. The url field is populated immediately with the asset's permanent CDN URL, which never changes. The upload response also includes id, name, libraryId, tags, contentType, and created. Fields computed asynchronously (width, height, size, blurhash, sizes, variants, description, updated, uploadedBy) are null in the upload response and are filled in once processing finishes, typically within seconds. Re-read the asset with GET /assets to get them.

When parentId is set, the returned asset is the new variant: its id is the variant's id, parentId is the asset it belongs to, and appUrl opens the parent asset with this variant selected. The variant is nested under the parent's variants; a GET /assets list returns the parent carrying it in that variants array.

Example

Terminal
curl --request POST \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Api-Version: 2026-07-08' \
  --url 'https://api.raster.app/organizations/acme-co/libraries/barcelona/assets' \
  --form 'files=@/path/to/photo-1.jpg' \
  --form 'files=@/path/to/photo-2.png'

To upload a file as a variant of an existing asset, pass parentId and a single file:

Terminal
curl --request POST \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Api-Version: 2026-07-08' \
  --url 'https://api.raster.app/organizations/acme-co/libraries/barcelona/assets' \
  --form 'files=@/path/to/variant.jpg' \
  --form 'parentId=<PARENT_ASSET_ID>'

The multipart field name must be files. Anything else is rejected with 400 and BAD_USER_INPUT. Per-file size and count limits map to 413 PAYLOAD_TOO_LARGE and 400 BAD_USER_INPUT respectively. See the Errors reference.

DELETE /organizations/:orgId/libraries/:libraryId/assets

Moves assets to trash by id, in batches. This is a soft delete, the same one the Raster app performs. The assets leave GET /assets and search immediately (both only return live assets), stay recoverable from trash, and are permanently removed after 30 days. Emptying the trash permanently is done in the Raster app.

Trashing an asset also trashes its variants. A variant cannot be trashed on its own, only together with its original, so a variant id in ids fails the whole call with 400 BAD_USER_INPUT before anything moves. Pass the original's id instead.

Parameters

Prop

Type

Returns

{ data: { success: boolean, message: string, ids: string[] } }:

  • ids: the asset ids this call moved to trash. Ids already in trash (an idempotent retry) or not in the library are skipped, so ids.length equals the count in message. When none of the ids names an asset in the library, the call fails with 400 BAD_USER_INPUT.
  • message: "Moved N assets to trash", where N is ids.length.
  • success: true in the normal case, including a call that changes nothing because every requested id was already trashed ("Moved 0 assets to trash", ids: []). It is false when some of the ids failed to move: the ids that did move are in ids, the rest are still live, and retrying the call moves them. The library's assetsCount and trashCount change only by the ids that moved.

Example

Terminal
curl --request DELETE \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Api-Version: 2026-07-08' \
  --header 'Content-Type: application/json' \
  --url 'https://api.raster.app/organizations/acme-co/libraries/barcelona/assets' \
  --data '{"ids":["abc123","def456"]}'

POST /organizations/:orgId/libraries/:libraryId/assets/tag

Applies one or more tags to a batch of assets in a single library. The call is idempotent on (asset, tag) pairs the asset already carries: re-running it changes nothing for those pairs.

Parameters

Prop

Type

Returns

{ data: { taggedCount: number } }. taggedCount is the number of (asset, tag) pairs the call added. Pairs the asset already carried are skipped and not counted. To check whether every requested pair was applied, compare taggedCount against assetIds.length × tags.length.

Example

Terminal
curl --request POST \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Api-Version: 2026-07-08' \
  --header 'Content-Type: application/json' \
  --url 'https://api.raster.app/organizations/acme-co/libraries/barcelona/assets/tag' \
  --data '{"assetIds":["abc123","def456"],"tags":["sunset","landscape"]}'

POST /organizations/:orgId/libraries/:libraryId/assets/untag

Removes one or more tags from a batch of assets in a single library. Like /assets/tag, the call is idempotent: pairs the asset doesn't carry are skipped.

Parameters

Prop

Type

Returns

{ data: { untaggedCount: number } }. untaggedCount is the number of (asset, tag) pairs the call removed.

Example

Terminal
curl --request POST \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Api-Version: 2026-07-08' \
  --header 'Content-Type: application/json' \
  --url 'https://api.raster.app/organizations/acme-co/libraries/barcelona/assets/untag' \
  --data '{"assetIds":["abc123"],"tags":["landscape"]}'

PATCH /organizations/:orgId/libraries/:libraryId/assets/:assetId/description

Replaces the description on a single asset. The value is stored verbatim, with no trimming and no AI rewrite. Pass an empty string to clear the field.

Parameters

Prop

Type

Returns

{ data: { assetId: string, description: string } }. The response echoes the asset id and the stored description, so a client can confirm what was stored without an extra read.

Example

Terminal
curl --request PATCH \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Api-Version: 2026-07-08' \
  --header 'Content-Type: application/json' \
  --url 'https://api.raster.app/organizations/acme-co/libraries/barcelona/assets/abc123/description' \
  --data '{"description":"Golden hour over the Mediterranean."}'

PATCH /organizations/:orgId/libraries/:libraryId/assets/:assetId/approval

Sets the approval of a single asset (an original or a variant), with an optional note. Each call replaces the asset's previous approval, including its note. The asset's uploader gets an email when it is set to approved or needs_changes, unless they set it themselves.

Send an OAuth access token as the bearer token. An API key can read an asset's approval but cannot set it: the request is rejected with 403 APPROVAL_REQUIRES_USER. The token's account needs access to the library at any role, Viewer included, and the organization needs a paid plan: without one the request is rejected with 403 PAID_PLAN_REQUIRED. When setting approval is not available to the token's account, the request is rejected with 403 FORBIDDEN.

A state outside the organization's approval states, a note over 500 characters, or an asset that is not in the library is rejected with 400 BAD_USER_INPUT before anything is written.

Parameters

Prop

Type

Returns

{ data: { assetId: string, approval: Approval } }. The response echoes the asset id and the Approval that was written, so a client can confirm what was stored without an extra read.

Example

Terminal
curl --request PATCH \
  --header 'Authorization: Bearer <ACCESS_TOKEN>' \
  --header 'Api-Version: 2026-07-08' \
  --header 'Content-Type: application/json' \
  --url 'https://api.raster.app/organizations/acme-co/libraries/barcelona/assets/abc123/approval' \
  --data '{"state":"needs_changes","note":"Crop tighter on the left."}'

POST /organizations/:orgId/libraries/:libraryId/assets/:assetId/promote

Makes one of the asset's variants its default: the variant's file replaces what the asset serves, at the same URL. Anything already embedding that URL shows the new file without being updated, so you can correct or replace a published asset without changing any link to it. Works the same for a photo and for a video.

Promoting bumps the asset's contentVersion.

The previous default is preserved as a new variant, so it can be promoted back, and keeps the name it had. The asset keeps its id and URL and takes the promoted variant's name. Its description and the tags generated from its image follow the image: the promoted variant hands over any it carries, and the preserved variant keeps the ones that described it. Tags you added yourself stay on the asset.

The promoted variant's own entry is removed, because its image is now the asset's.

Rejected with 400 BAD_USER_INPUT when the id names an original rather than a variant, when the variant is archived or still processing, when the asset is not live, when the asset is itself still being processed (a video whose poster is not ready yet), when the asset is neither an image nor a video, or when the variant is a different kind of media than the asset. A variant that is not in the library, or is in the trash, returns 404 NOT_FOUND. If another promote is already running on the same asset, the request is rejected with 409 CONFLICT. Wait for it to finish, then retry.

Parameters

Prop

Type

Returns

{ data: { assetId: string, promotedVariantId: string, demotedVariantId: string } }. All three are echoed so you can update local state without a follow-up read: demotedVariantId is the new variant holding the previous default's image.

The response confirms the promote is accepted and echoes the three ids; the image swap and the removal of the promoted variant's entry complete a moment later. Re-fetch the asset after a short delay to see the change. Its variants list is final once the swap finishes.

Because the URL doesn't change, a cached copy of the old image may still be served for a short time. Append the asset's contentVersion to the URL as ?v= to render the new image.

Example

Terminal
curl --request POST \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Api-Version: 2026-07-08' \
  --header 'Content-Type: application/json' \
  --url 'https://api.raster.app/organizations/acme-co/libraries/barcelona/assets/abc123/promote' \
  --data '{"variantId":"var789"}'

POST /organizations/:orgId/libraries/:libraryId/assets/transfer

Moves a batch of assets from one library to another within the same organization. The URL's :libraryId is the source library; the body names the target. A target library outside the organization fails with 404 API_KEY_NOT_AUTHORIZED_FOR_LIBRARY. When source and target are the same, the call changes nothing (transferredCount: 0).

Parameters

Prop

Type

Returns

{ data: { transferredCount: number, sourceLibraryId: string, targetLibraryId: string } }. Source and target are echoed so a client can confirm both ends without an extra fetch.

Example

Terminal
curl --request POST \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Api-Version: 2026-07-08' \
  --header 'Content-Type: application/json' \
  --url 'https://api.raster.app/organizations/acme-co/libraries/barcelona/assets/transfer' \
  --data '{"targetLibraryId":"lisbon","assetIds":["abc123","def456"]}'

On this page