generated: '2026-08-27' method: searched source: https://docs.pexafy.com/pagination additional_sources: - https://docs.pexafy.com/errors - https://docs.pexafy.com/quickstart - openapi/pexafy-api-openapi.json - openapi/pexafy-api-v1-openapi.json authentication: style: api-key header header: x-api-key alternative: "Authorization: Bearer " key_prefix: pexafy_api_ oauth: OAuth 2.1 authorization-code + PKCE, used by the MCP server (see authentication/pexafy-authentication.yml) detail: authentication/pexafy-authentication.yml base_url: live: https://api.pexafy.com path_prefix: /api/v1 effective: https://api.pexafy.com/api/v1 note: >- The two published specs express this differently — the live schema has servers[0].url = https://api.pexafy.com with /api/v1 in every path, while the versioned description has servers[0].url = https://api.pexafy.com/api/v1 with bare paths. Same effective URL; a reader comparing the two documents must not concatenate them. envelope: shape: "{success, data, meta, pagination, error}" success_flag: success (boolean) payload: data meta: "{request_id, took_ms}" error: "{code, message, request_id} — see errors/pexafy-problem-types.yml" note: One envelope for successes and errors alike; branch on `success` then on `error.code`. pagination: style: cursor request_params: - name: per_page range: 1-100 default: 20 note: >- Set on the first request only; following pages keep that page size. The default moved from 24 to 20 in description 1.3.0 — a silent behaviour change for every caller who never set it, which the provider's own changelog flags as "notable" rather than additive. - name: cursor note: opaque token taken from pagination.next_cursor response_fields: - pagination.per_page - pagination.has_more - pagination.next_cursor cursor_ttl: ~5 minutes (stated in the docs) cursor_semantics: >- The cursor carries the query and every filter, including an uploaded image — a subsequent page of an image search needs only cursor=, no re-upload. Changing the query between pages is not supported; start again from the first page. paginated_operations: - search_photos_api_v1_search_photos_get - search_photos_by_image_api_v1_search_photos_post - photo_similar_api_v1_photos__photo_id__similar_get non_paginated: facets, collections and usage return their full list directly field_expansion: supported: false plan_gating: >- There is no expand/fields parameter. Response richness is a BILLING dimension instead: Free and Starter get the core response shape, Pro and above unlock 6 enriched fields, and asking for a field outside your plan returns 403 PLAN_RESTRICTION. metadata: user_defined: false note: No customer-writable metadata surface. Collections are the only user-owned objects. request_tracing: header: x-request-id body_field: meta.request_id (and error.request_id, same value) generated_by: server note: >- Observed live on every response. The docs instruct quoting meta.request_id when contacting support, and the official Python SDK exposes it on raised exceptions. versioning: scheme: uri-path current: v1 description_version: 1.3.0 detail: lifecycle/pexafy-lifecycle.yml rate_limit_signaling: headers: [x-ratelimit-limit, x-ratelimit-remaining, x-ratelimit-reset] status: 429 detail: rate-limits/pexafy-rate-limits.yml idempotency: supported: false key_header: null note: >- NO idempotency contract. Neither published spec declares an Idempotency-Key parameter and the docs never mention one. The write surface is small and mostly naturally idempotent by shape — adding a photo already in a collection returns 409 PHOTO_ALREADY_IN_COLLECTION rather than duplicating it, and DELETE is idempotent — but POST /api/v1/collections will create a second identically-named collection if a retry lands twice. No `Idempotency` pointer is emitted for this provider. dry_run_mode: supported: false note: No preview/simulate mode on any operation. reversibility: grade: documented grade_basis: >- Every write operation has a real reversal operation published in the same contract, but the docs state no window for any of them — no retention period, no restore-after-delete, no undo deadline. That is a reversal path without a window, which the rubric grades `documented` rather than `verified`. Nothing in the docs states a window, so none is asserted here. read_only_api: false write_surface: - operation: create_collection_api_v1_collections_post action: Create a collection reversal: delete_collection_api_v1_collections__collection_id__delete reversal_action: Delete the collection window: null window_source: null note: >- Reversal is deletion, not restoration — undoing a create removes the object outright. A retry-created duplicate collection is cleaned up the same way. - operation: add_photo_to_collection_api_v1_collections__collection_id__photos_post action: Add a photo to a collection reversal: remove_photo_from_collection_api_v1_collections__collection_id__photos__photo_id__delete reversal_action: Remove the photo from the collection window: null window_source: null note: A true symmetric pair — add and remove fully invert each other with no state left behind. - operation: delete_collection_api_v1_collections__collection_id__delete action: Delete a collection reversal: null reversal_action: null window: null window_source: null note: >- IRREVERSIBLE, and the provider says so in the contract. The operation's own description reads verbatim: "Permanently delete a collection and remove every photo saved in it. This cannot be undone (the original photos in the library are not affected)." There is no restore, undelete or trash operation in either published spec. The only recovery is to recreate the collection and re-add every photo by id. - operation: remove_photo_from_collection_api_v1_collections__collection_id__photos__photo_id__delete action: Remove a photo from a collection reversal: add_photo_to_collection_api_v1_collections__collection_id__photos_post reversal_action: Add the photo back window: null window_source: null note: Reversible as long as the caller still holds the photo id. blast_radius: >- Low. Nothing in this API moves money, sends a message, or changes anything outside the caller's own account. The worst irreversible outcome is the loss of a saved set of photo ids. The MCP server — the surface most likely to be driven autonomously — exposes no write tools at all, so an agent reaching Pexafy through MCP cannot take any irreversible action. agent_guidance: >- Before DELETE /api/v1/collections/{collection_id}, read the collection's photos with GET /api/v1/collections/{collection_id} and keep data.photos[].photo_id; that list is the only thing that makes the delete recoverable. There is no separate list-collection-photos operation — POST is the only method on /api/v1/collections/{collection_id}/photos. cross_links: errors: errors/pexafy-problem-types.yml lifecycle: lifecycle/pexafy-lifecycle.yml authentication: authentication/pexafy-authentication.yml rate_limits: rate-limits/pexafy-rate-limits.yml scopes: scopes/pexafy-scopes.yml