# ZoomHub API reference: Collections Part of the [ZoomHub API reference](https://zoomhub.net/docs/api.md), whose index covers authentication, errors and the other resources. > **Beta:** The v2 API is in beta: endpoints, attributes and error codes may still change before it is final. Questions or feedback: [daniel@zoomhub.net](mailto:daniel@zoomhub.net). Base URL: `https://api.zoomhub.net`. Send your API key as `Authorization: Bearer `; see [Authentication](https://zoomhub.net/docs/api.md#authentication). ## Collections A collection is an ordered set of images that zoom together as one Deep Zoom Collection: ZoomHub composites every member’s tiles into one pyramid, so a viewer can show hundreds of images at once. A PDF becomes a collection of its pages, and you can make your own from images you uploaded. A collection holds only its owner’s images, whoever adds them, and as many as the owner’s plan holds: up to 1,000 on Business. Add, remove and order members with the [collection item](https://zoomhub.net/docs/api/collection-items.md#collection-items) endpoints, and show the collection with a [gallery viewer](https://zoomhub.net/docs/api/viewers.md#viewers). ### The collection object An ordered set of images, and the composite they zoom in. **Attributes** - `object` (string): Always `collection`. - `id` (string): The collection’s ID. - `title` (string, nullable): The collection’s title, or `null` for an untitled one. - `source` (object, nullable): Where the collection came from, such as the PDF its pages were split from, or `null` for a collection made of images. - `type` (enum): The kind of source, which is also the key its details are under. - `url`: A document fetched from the web; its details are under `url`. - `upload`: A document uploaded as a file. `upload` is an empty object: an upload’s file isn’t exposed. - `url` (object, optional): Present when `type` is `url`. - `original_url` (string): The document’s URL. - `upload` (object, optional): Present, and empty, when `type` is `upload`. - `status` (enum): How processing its members went. - `queued`: The collection has no members yet. - `processing`: Some members are still being processed. - `succeeded`: Every member was processed. - `failed`: Every member finished, and at least one failed. - `num_items` (integer): How many members it has. - `dzc` (object): The Deep Zoom Collection that composites the members’ tiles, for viewers that read DZC. - `url` (string): The DZC manifest’s URL. It stays the same as members come and go; don’t store it, since it changes when the collection’s protection does. - `tile_size` (integer): The edge of a tile in pixels. - `max_level` (integer): The deepest level of the composite. Down to it, viewers draw members from the composite’s tiles; deeper, from each member’s own. - `tile_format` (string): The tiles’ image format, which is also their file extension, such as `webp`. - `status` (enum): Whether the composite is up to date with the members. - `processing`: Tiles are still being composited, after a member was added or removed. - `succeeded`: The composite is up to date. - `links` (object): Where the collection lives. - `self` (string): This collection in the API. - `share` (string): The collection’s page on ZoomHub. **Example 1** ```json { "dzc": { "max_level": 9, "status": "succeeded", "tile_format": "webp", "tile_size": 512, "url": "https://cache.zoomhub.net/collections/Smp7Set9Abc.dzc" }, "id": "Smp7Set9Abc", "links": { "self": "https://api.zoomhub.net/v2/collections/Smp7Set9Abc", "share": "https://zoomhub.net/c/Smp7Set9Abc" }, "num_items": 2, "object": "collection", "source": { "type": "url", "url": { "original_url": "https://example.com/doc.pdf" } }, "status": "succeeded", "title": null } ``` **Example 2** ```json { "dzc": { "max_level": 9, "status": "succeeded", "tile_format": "webp", "tile_size": 512, "url": "https://cache.zoomhub.net/collections/Smp7Set9Abc.dzc" }, "id": "Smp7Set9Abc", "links": { "self": "https://api.zoomhub.net/v2/collections/Smp7Set9Abc", "share": "https://zoomhub.net/c/Smp7Set9Abc" }, "num_items": 0, "object": "collection", "source": { "type": "upload", "upload": {} }, "status": "queued", "title": "Sample set" } ``` **Example 3** ```json { "dzc": { "max_level": 9, "status": "succeeded", "tile_format": "webp", "tile_size": 512, "url": "https://cache.zoomhub.net/collections/Smp7Set9Abc.dzc" }, "id": "Smp7Set9Abc", "links": { "self": "https://api.zoomhub.net/v2/collections/Smp7Set9Abc", "share": "https://zoomhub.net/c/Smp7Set9Abc" }, "num_items": 0, "object": "collection", "source": null, "status": "queued", "title": "Sample set" } ``` ### Create a collection — `POST /v2/collections` **Authentication:** API key. Send an API key. Creates a collection, with its first members if you list them. The create is all or nothing: every listed image is checked before anything is written, so a refused one leaves no collection behind. Images still processing join too, and are composited once they are done. **Body parameters** - `title` (string, optional): The collection’s title. At most 250 characters on a single line, and not empty; surrounding whitespace is trimmed. - `source_url` (string, optional): The URL of the document the collection was made from, starting with `http://` or `https://`. It is recorded as the collection’s `source`; ZoomHub doesn’t fetch it. - `items` (array of objects, optional): The first members, in display order. Without it, the collection starts empty. At most 10,000 entries, none listed twice. An image that is also a member of a listed collection, or of two, joins once. - `asset_id` (string, optional): An image to add, by its content ID. Send this or `collection_id`, not both. - `collection_id` (string, optional): A collection whose members to add, in its order, as they are now: a PDF’s collection adds its pages. Members it gains or loses later don’t follow. A listed collection’s members that failed to process are left out. **Responses** - `201 Created`: The new collection object. - `400 Bad Request`, error code `bad_request`: The body isn’t JSON, misses a required key, has a key the endpoint doesn’t know, or breaks a limit. The detail says which. - `400 Bad Request`, error code `asset_processing_failed`: A listed image failed to process. - `401 Unauthorized`, error code `not_authenticated`: No `Authorization` header. - `401 Unauthorized`, error code `invalid_api_key`: The key is unknown or revoked, or the header isn’t a `Bearer` key. - `403 Forbidden`, error code `plan_required`: The key is valid, but its account’s plan doesn’t include the API. - `403 Forbidden`, error code `forbidden`: The key’s account doesn’t own a listed image. - `403 Forbidden`, error code `forbidden`: A listed image belongs to someone other than the collection’s owner. Only the owner’s images can join. - `404 Not Found`, error code `content_not_found`: A listed image doesn’t exist. - `404 Not Found`, error code `not_found`: A listed `collection_id` isn’t a collection the key’s account owns. - `422 Unprocessable Entity`, error code `collection_full`: The collection would hold more images than its owner’s plan allows (1,000 on Business). The detail names the limit and the plan that holds more. - `503 Service Unavailable`, error code `protection_pending`: The change involves a Protected image and the CDN couldn’t be updated; nothing was saved. Retry. - `503 Service Unavailable`, error code `service_unavailable`: The collection is busy updating its protection; nothing was saved. Retry after the seconds the `Retry-After` header gives. **Request** ```bash curl "https://api.zoomhub.net/v2/collections" \ --header "Authorization: Bearer $ZOOMHUB_API_KEY" \ --header "Content-Type: application/json" \ --data '{ "items": [ { "asset_id": "smpAsset1" }, { "asset_id": "smpAsset2" } ], "source_url": "https://example.com/doc.pdf" }' ``` **Response** `201 Created` ```json { "dzc": { "max_level": 9, "status": "succeeded", "tile_format": "webp", "tile_size": 512, "url": "https://cache.zoomhub.net/collections/Smp7Set9Abc.dzc" }, "id": "Smp7Set9Abc", "links": { "self": "https://api.zoomhub.net/v2/collections/Smp7Set9Abc", "share": "https://zoomhub.net/c/Smp7Set9Abc" }, "num_items": 2, "object": "collection", "source": { "type": "url", "url": { "original_url": "https://example.com/doc.pdf" } }, "status": "succeeded", "title": null } ``` ### Retrieve a collection — `GET /v2/collections/{id}` **Authentication:** API key optional. Works without a key; a key opens your own Protected items. Returns the collection with the given ID. Anyone may read a collection unless one of its members is Protected. Send your key to read your own: the answer then carries `Cache-Control: no-store, private`. Poll it while `status` or `dzc.status` is `processing`. > **Note:** Reading what isn’t Protected needs no key: leave out the `Authorization` header. A key that fails to verify counts as no key: what’s public answers as usual, and what’s Protected answers `403` `forbidden`. **Path parameters** - `id` (string, required): The collection’s ID. **Responses** - `200 OK`: The collection object. - `403 Forbidden`, error code `forbidden`: The collection is Protected and the key’s account may not view it. The answer is the same whatever the reason, so it doesn’t reveal which rule refused. - `404 Not Found`, error code `not_found`: No collection has this ID, or it was deleted. **Request** ```bash curl "https://api.zoomhub.net/v2/collections/Smp7Set9Abc" ``` **Response** `200 OK` ```json { "dzc": { "max_level": 9, "status": "succeeded", "tile_format": "webp", "tile_size": 512, "url": "https://cache.zoomhub.net/collections/Smp7Set9Abc.dzc" }, "id": "Smp7Set9Abc", "links": { "self": "https://api.zoomhub.net/v2/collections/Smp7Set9Abc", "share": "https://zoomhub.net/c/Smp7Set9Abc" }, "num_items": 2, "object": "collection", "source": { "type": "url", "url": { "original_url": "https://example.com/doc.pdf" } }, "status": "succeeded", "title": null } ``` ### Update a collection — `PATCH /v2/collections/{id}` **Authentication:** API key. Send an API key. Renames a collection. Only the collection’s owner, or an admin, may update it. **Path parameters** - `id` (string, required): The collection’s ID. **Body parameters** - `title` (string, optional, nullable): The new title; `null` removes it. At most 250 characters on a single line, and not empty; surrounding whitespace is trimmed. **Responses** - `200 OK`: The updated collection object. - `400 Bad Request`, error code `bad_request`: The body isn’t JSON, misses a required key, has a key the endpoint doesn’t know, or breaks a limit. The detail says which. - `401 Unauthorized`, error code `not_authenticated`: No `Authorization` header. - `401 Unauthorized`, error code `invalid_api_key`: The key is unknown or revoked, or the header isn’t a `Bearer` key. - `403 Forbidden`, error code `plan_required`: The key is valid, but its account’s plan doesn’t include the API. - `403 Forbidden`, error code `forbidden`: The key’s account neither owns the collection nor is an admin. - `404 Not Found`, error code `not_found`: No collection has this ID, or it was deleted. **Request** ```bash curl "https://api.zoomhub.net/v2/collections/Smp7Set9Abc" \ --request PATCH \ --header "Authorization: Bearer $ZOOMHUB_API_KEY" \ --header "Content-Type: application/json" \ --data '{ "title": "Sample set" }' ``` **Response** `200 OK` ```json { "dzc": { "max_level": 9, "status": "succeeded", "tile_format": "webp", "tile_size": 512, "url": "https://cache.zoomhub.net/collections/Smp7Set9Abc.dzc" }, "id": "Smp7Set9Abc", "links": { "self": "https://api.zoomhub.net/v2/collections/Smp7Set9Abc", "share": "https://zoomhub.net/c/Smp7Set9Abc" }, "num_items": 0, "object": "collection", "source": null, "status": "queued", "title": "Sample set" } ``` ### Delete a collection — `DELETE /v2/collections/{id}` **Authentication:** API key. Send an API key. Deletes a collection, but not its members. Only the collection’s owner, or an admin, may delete it. From then on the collection answers `404`, here and on its page. Its member images stay as they are. **Path parameters** - `id` (string, required): The collection’s ID. **Responses** - `204 No Content`: Deleted. - `401 Unauthorized`, error code `not_authenticated`: No `Authorization` header. - `401 Unauthorized`, error code `invalid_api_key`: The key is unknown or revoked, or the header isn’t a `Bearer` key. - `403 Forbidden`, error code `plan_required`: The key is valid, but its account’s plan doesn’t include the API. - `403 Forbidden`, error code `forbidden`: The key’s account neither owns the collection nor is an admin. - `404 Not Found`, error code `not_found`: No collection has this ID, or it was deleted. **Request** ```bash curl "https://api.zoomhub.net/v2/collections/Smp7Set9Abc" \ --request DELETE \ --header "Authorization: Bearer $ZOOMHUB_API_KEY" ```