Pexafy Photos API

One photo at a time.

Operations 4

GET /photos/{photo_id} Fetch one photo #
GET /photos/{photo_id}/similar Photos that look like this one #
GET /api/v1/photos/{photo_id} Get a photo #
GET /api/v1/photos/{photo_id}/similar Find similar photos #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/pexafy-photos-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no email required.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

pexafy-photos-api-openapi.yml Raw ↑
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