openapi: 3.2.0 info: title: Pexafy Photos API x-refined-note: - x-logo differs across the merged source definitions and was not carried version: '1.0' description: 'Operations tagged photos across 2 of this provider''s published API definitions: pexafy-openapi-v1.3.0.json, pexafy-openapi.json. Each path carries the servers of the definition it was published in.' servers: - url: https://api.pexafy.com/api/v1 description: Production - url: https://api.pexafy.com description: Production tags: - name: photos description: One photo at a time. paths: /photos/{photo_id}: get: operationId: getPhoto tags: - photos summary: Fetch one photo parameters: - name: photo_id in: path description: UUIDv7 of the photo. schema: type: string format: uuid required: true responses: '200': description: The photo. content: application/json: schema: $ref: '#/components/schemas/PhotoEnvelope' '401': description: The API key is missing, malformed or revoked. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' example: success: false error: code: unauthenticated message: The API key is missing, malformed or revoked. request_id: req_01J8Z3 '404': description: No such resource. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' example: success: false error: code: not_found message: No such resource. request_id: req_01J8Z3 '429': description: Too many requests, or the plan quota is exhausted. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' example: success: false error: code: rate_limited message: Too many requests, or the plan quota is exhausted. request_id: req_01J8Z3 security: - apiKey: [] servers: - url: https://api.pexafy.com/api/v1 description: Production /photos/{photo_id}/similar: get: operationId: getSimilarPhotos tags: - photos summary: Photos that look like this one description: Nearest neighbours of the photo's own image vector. The photo itself is excluded from its own results, which is worth knowing if you are counting on per_page results and getting per_page minus one. parameters: - name: photo_id in: path description: UUIDv7 of the photo. schema: type: string format: uuid required: true - name: color_name in: query description: Restrict to one dominant colour. The API filters on a single colour; passing several is rejected rather than silently using the last one. schema: $ref: '#/components/schemas/ColorName' - name: color_hex in: query description: Restrict to photos near one hex colour, used with color_tolerance. schema: type: string pattern: ^#?[0-9a-fA-F]{6}$ - name: color_tolerance in: query description: How far from color_hex a photo may sit, in the same units as the colour distance the indexer stores. schema: type: integer minimum: 0 maximum: 100 default: 20 - name: orientation in: query description: One or more orientations. Repeat the parameter or pass a comma separated list. schema: type: array items: $ref: '#/components/schemas/Orientation' maxItems: 3 - name: source in: query description: Restrict to one or more upstream libraries. schema: type: array items: $ref: '#/components/schemas/Source' maxItems: 8 - name: license_type in: query description: Restrict to one or more licence families. schema: type: array items: $ref: '#/components/schemas/LicenseType' maxItems: 8 - name: photographer in: query description: Restrict to one photographer, by username. schema: type: string maxLength: 120 - name: per_page in: query description: Results per page. schema: type: integer minimum: 1 maximum: 80 default: 20 - name: limit in: query description: Hard ceiling on results across all pages of this query. schema: type: integer minimum: 1 maximum: 200 - name: score_threshold in: query description: Drop results whose relevance score is below this. Search is semantic, so every query returns something; this is how you decide it returned nothing useful. schema: type: number minimum: 0 maximum: 1 - name: cursor in: query description: Opaque cursor from pagination.next_cursor. Cursors are tied to the query that produced them and expire; treat them as single use. schema: type: string maxLength: 512 - name: fields in: query description: Comma separated list of Photo fields to return. Trims the payload, does not change ranking. schema: type: string - name: after_date in: query description: Only photos uploaded on or after this date. schema: type: string format: date - name: sort_by in: query description: Ordering. Defaults to relevance. schema: $ref: '#/components/schemas/SortBy' responses: '200': description: Neighbours, closest first. headers: X-Request-Id: description: Echoed in error payloads; quote it in a support request. schema: type: string X-RateLimit-Remaining: description: Calls left in the current window. schema: type: integer content: application/json: schema: $ref: '#/components/schemas/SearchEnvelope' example: success: true data: - photo_id: 0192f3a1-6c1e-7a3b-9f2d-4c8e1b5a7d20 image_url: https://cdn.pexafy.com/p/0192f3a1/regular.jpg urls: thumb: https://cdn.pexafy.com/p/0192f3a1/thumb.jpg small: https://cdn.pexafy.com/p/0192f3a1/small.jpg regular: https://cdn.pexafy.com/p/0192f3a1/regular.jpg large: https://cdn.pexafy.com/p/0192f3a1/large.jpg full: https://cdn.pexafy.com/p/0192f3a1/full.jpg width: 5472 height: 3648 orientation: landscape color_name: blue color_hex: '#2f4f6f' photographer_username: a-photographer source: unsplash license_type: unsplash alt_description: an empty office lit only by monitors relevance_score: 0.71 pagination: next_cursor: c2NvcmU6MC42MQ per_page: 20 has_more: true meta: request_id: req_01J8Z3 took_ms: 61.4 '401': description: The API key is missing, malformed or revoked. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' example: success: false error: code: unauthenticated message: The API key is missing, malformed or revoked. request_id: req_01J8Z3 '404': description: No such resource. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' example: success: false error: code: not_found message: No such resource. request_id: req_01J8Z3 '422': description: A parameter was rejected before the search ran. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' example: success: false error: code: validation_failed message: A parameter was rejected before the search ran. request_id: req_01J8Z3 '429': description: Too many requests, or the plan quota is exhausted. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' example: success: false error: code: rate_limited message: Too many requests, or the plan quota is exhausted. request_id: req_01J8Z3 security: - apiKey: [] servers: - url: https://api.pexafy.com/api/v1 description: Production /api/v1/photos/{photo_id}: get: tags: - photos summary: Get a photo operationId: get_photo_api_v1_photos__photo_id__get parameters: - name: photo_id in: path required: true schema: type: string title: Photo Id description: The photo's unique Pexafy identifier (a UUID), as returned by search. - name: fields in: query required: false schema: anyOf: - type: string - type: 'null' title: Fields description: Comma-separated list to return only the fields you need (e.g. `photo_id,urls,attribution`). Omit to get everything. responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/PhotoResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - ApiKeyAuth: [] description: 'Retrieve the full details of a single photo by its Pexafy `photo_id` (the identifier returned by every search result). You get everything search returns, plus extra metadata when available: exact `width` and `height`, the original `source_image_url`, the photographer''s `full_name` and profile `url`, and the upload date (`uploaded_on`). The `urls` object gives you the image in five ready-to-use sizes.' servers: - url: https://api.pexafy.com description: Production /api/v1/photos/{photo_id}/similar: get: tags: - photos summary: Find similar photos operationId: photo_similar_api_v1_photos__photo_id__similar_get parameters: - name: photo_id in: path required: true schema: type: string title: Photo Id description: The reference photo's unique Pexafy identifier (a UUID). - name: per_page in: query required: false schema: type: integer maximum: 100 minimum: 1 default: 20 title: Per Page description: Number of similar photos to return, from 1 to 100. Defaults to 20. - name: limit in: query required: false schema: anyOf: - type: integer maximum: 100 minimum: 1 - type: 'null' description: Maximum TOTAL number of similar photos across all pages — NOT the page size (use `per_page` for that). Omit for no cap. title: Limit description: Maximum TOTAL number of similar photos across all pages — NOT the page size (use `per_page` for that). Omit for no cap. - name: cursor in: query required: false schema: anyOf: - type: string - type: 'null' description: Token to fetch the next page. Take `pagination.next_cursor` from a previous response and pass it back here. See the Pagination guide. title: Cursor description: Token to fetch the next page. Take `pagination.next_cursor` from a previous response and pass it back here. See the Pagination guide. responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/PhotoListResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - ApiKeyAuth: [] description: Return photos that look similar to a given one — a quick way to offer alternatives or build a visually consistent set. The reference photo itself is never included in the results. Each result has the same fields as a search result. servers: - url: https://api.pexafy.com description: Production components: schemas: Meta: type: object properties: request_id: type: string took_ms: type: number description: Server side time, excluding transfer. SearchEnvelope: type: object required: - success - data - pagination properties: success: type: boolean data: type: array items: $ref: '#/components/schemas/Photo' pagination: $ref: '#/components/schemas/Pagination' meta: $ref: '#/components/schemas/Meta' PhotoUrls: type: object required: - thumb - small - regular - large - full description: The same image at five widths. Pick the smallest one that fits; the CDN charges you nothing for it but the upstream library counts a view either way. properties: thumb: type: string format: uri small: type: string format: uri regular: type: string format: uri large: type: string format: uri full: type: string format: uri Attribution: type: object description: Credit line, ready to drop into a page. Most upstream licences require it even when they do not require a link. properties: html: type: string plain: type: string ColorName: type: string enum: - black - white - grey - red - orange - yellow - green - teal - blue - purple - pink - brown x-extensible-enum: true description: Extensible. SortBy: type: string enum: - relevance - recent - popular default: relevance x-extensible-enum: true description: Extensible. ErrorEnvelope: type: object required: - success - error description: Every failure uses this shape, whatever the status code. properties: success: type: boolean enum: - false error: type: object required: - code - message properties: code: type: string description: 'Machine readable and stable across versions. Branch on this, not on the message. The set is extensible: treat an unrecognised code as a generic failure of its status class.' example: validation_failed message: type: string description: Human readable, not stable. request_id: type: string field: type: - string - 'null' description: Which parameter was rejected, when the failure is about one. Pagination: type: object required: - per_page - has_more description: 'Cursor paging. There is no total count: the result set is a ranked stream and counting it would cost more than returning it.' properties: next_cursor: type: - string - 'null' per_page: type: integer has_more: type: boolean LicenseType: type: string enum: - cc0 - unsplash - pexels - pixabay - cc-by - cc-by-sa x-extensible-enum: true description: Extensible. PhotoEnvelope: type: object required: - success - data properties: success: type: boolean data: $ref: '#/components/schemas/Photo' meta: $ref: '#/components/schemas/Meta' example: success: true data: photo_id: 0192f3a1-6c1e-7a3b-9f2d-4c8e1b5a7d20 urls: regular: https://cdn.pexafy.com/p/0192f3a1/regular.jpg source: unsplash license_type: unsplash meta: request_id: req_01J8Z3 took_ms: 4.1 Orientation: type: string enum: - landscape - portrait - square x-extensible-enum: true description: 'Extensible: new orientations may be added.' Photo: type: object required: - photo_id - urls - source - license_type properties: photo_id: type: string format: uuid description: UUIDv7. Sortable by creation time, which is why it is not an integer. image_url: type: string format: uri urls: $ref: '#/components/schemas/PhotoUrls' width: type: integer minimum: 1 height: type: integer minimum: 1 blur_hash: type: - string - 'null' description: Placeholder while loading. orientation: $ref: '#/components/schemas/Orientation' color_name: $ref: '#/components/schemas/ColorName' color_hex: type: string photographer_username: type: string photographer_full_name: type: - string - 'null' photographer_url: type: - string - 'null' format: uri source: $ref: '#/components/schemas/Source' license_type: $ref: '#/components/schemas/LicenseType' source_image_url: type: - string - 'null' format: uri source_description: type: - string - 'null' description: type: - string - 'null' alt_description: type: - string - 'null' description: Short text for an alt attribute. Null for a large part of the catalogue; fall back to description, then to source_description, then to nothing rather than to the filename. uploaded_on: type: - string - 'null' format: date relevance_score: type: - number - 'null' minimum: 0 maximum: 1 description: 'Only present on search results. Comparable within one response and not across responses: the scores are normalised per query.' attribution: $ref: '#/components/schemas/Attribution' example: photo_id: 0192f3a1-6c1e-7a3b-9f2d-4c8e1b5a7d20 urls: regular: https://cdn.pexafy.com/p/0192f3a1/regular.jpg source: unsplash license_type: unsplash Source: type: string enum: - pexels - unsplash - pixabay - wikimedia x-extensible-enum: true description: 'Upstream library. Extensible: this list grows as libraries are indexed, so parse it as an open string.' Meta_2: type: object properties: request_id: type: string description: Unique id for this request (quote it in support tickets). took_ms: type: number description: Server processing time in milliseconds. PhotoUrls_2: type: object description: Ready-to-use image links in five sizes. properties: thumb: type: string format: uri small: type: string format: uri regular: type: string format: uri large: type: string format: uri full: type: string format: uri Attribution_2: type: object description: Ready-to-display credit for the photographer/source. properties: html: type: string description: HTML attribution snippet. plain: type: string description: Plain-text attribution. PhotoListResponse: type: object properties: success: type: boolean data: type: array items: $ref: '#/components/schemas/Photo_2' meta: $ref: '#/components/schemas/Meta_2' error: anyOf: - $ref: '#/components/schemas/ApiError' - type: 'null' pagination: anyOf: - $ref: '#/components/schemas/Pagination_2' - type: 'null' Pagination_2: type: object properties: next_cursor: type: - string - 'null' description: Pass back as `cursor` for the next page; `null` when `has_more` is false. per_page: type: integer description: Number of items per page. has_more: type: boolean description: Whether another page exists. PhotoResponse: type: object properties: success: type: boolean data: $ref: '#/components/schemas/Photo_2' meta: $ref: '#/components/schemas/Meta_2' error: anyOf: - $ref: '#/components/schemas/ApiError' - type: 'null' Photo_2: type: object description: A photo result. Fields returned can be narrowed with the `fields` parameter and may depend on your plan. properties: photo_id: type: string description: Unique Pexafy identifier (UUID). image_url: type: string format: uri description: Canonical source image URL. urls: $ref: '#/components/schemas/PhotoUrls_2' width: type: - integer - 'null' height: type: - integer - 'null' blur_hash: type: - string - 'null' description: BlurHash placeholder string. orientation: type: string enum: - landscape - portrait - square color_name: type: string description: Dominant color name. color_hex: type: string description: Dominant color hex code. photographer_username: type: string photographer_full_name: type: - string - 'null' photographer_url: type: - string - 'null' format: uri source: type: string description: Provider (e.g. `Pexels`, `Unsplash`, `Pixabay`). license_type: type: string description: License type (e.g. `free`). source_image_url: type: - string - 'null' format: uri description: URL of the photo's page on the provider. source_description: type: - string - 'null' description: type: - string - 'null' description: AI-generated caption. alt_description: type: - string - 'null' description: Accessibility-friendly text. uploaded_on: type: - string - 'null' description: Publication date (YYYY-MM-DD). relevance_score: type: - number - 'null' description: Match score 0–1 (higher is better). Only on search results. attribution: $ref: '#/components/schemas/Attribution_2' ApiError: type: object properties: code: type: string description: Machine-readable error code (e.g. `MISSING_PARAMS`, `PHOTO_NOT_FOUND`). message: type: string description: Human-readable error message. request_id: type: string required: - code - message HTTPValidationError: properties: detail: items: $ref: '#/components/schemas/ValidationError' type: array title: Detail type: object title: HTTPValidationError ValidationError: properties: loc: items: anyOf: - type: string - type: integer type: array title: Location msg: type: string title: Message type: type: string title: Error Type type: object required: - loc - msg - type title: ValidationError securitySchemes: apiKey: type: http scheme: bearer description: Send the key as a bearer token. Keys carry scopes; a read scoped key gets a 403 on anything under /collections. ApiKeyAuth: type: apiKey in: header name: x-api-key description: 'Pass your Pexafy API key in this header. Get your API key at [pexafy.com/dashboard/api-keys/create](https://pexafy.com/dashboard/api-keys/create/).' x-refined-from: - pexafy-openapi-v1.3.0.json - pexafy-openapi.json