Skip to main content

pexafy-python

Python client for the Pexafy image search API. Search a catalogue of free stock photos by describing what you want, or by handing it an image to match.

pip install pexafy

Getting started

from pexafy import Client

client = Client("your-api-key")

for photo in client.search("a quiet street in the rain", per_page=5):
    print(photo.urls.regular, "-", photo.alt_text)

The key comes from your dashboard; the free tier does not ask for a card. If you would rather not put it in the code, the client picks up PEXAFY_API_KEY from the environment.

Writing queries

Search runs on meaning, not keywords, so full sentences work better than a pile of nouns. two people hiking on a ridge at dawn finds what you would expect; hiking dawn people gives you a worse ranking, because you have thrown away the relationships between the words.

Filters narrow the result set after the semantic match:

result = client.search(
    "an empty office at night",
    orientation="landscape",
    color_name="blue",
    source=["Pexels", "Unsplash"],
    per_page=20,
)

print(len(result), "photos in", result.took_ms, "ms")

List filters accept either a list or a comma separated string.

Paging

A single call returns one page. iter_search follows the cursor for you and yields photos until the results run out or you have seen enough:

for photo in client.iter_search("vintage typewriter", max_results=200):
    download(photo.urls.large)

Search by image

Pass a path, raw bytes, or an open file:

similar = client.search_by_image("moodboard/reference.jpg", per_page=12)

If you already have a photo id, client.similar(photo_id) is cheaper — the image does not have to be uploaded and encoded again.

Async

The same surface, awaitable:

import asyncio
from pexafy import AsyncClient

async def main():
    async with AsyncClient() as client:
        pages = await asyncio.gather(
            client.search("desert road"),
            client.search("snow covered pines"),
        )
    for page in pages:
        print(len(page))

asyncio.run(main())

Errors

Everything raised inherits from PexafyError. The ones worth catching separately:

from pexafy import errors

try:
    client.search("...")
except errors.RateLimitError as exc:
    time.sleep(exc.retry_after or 60)
except errors.AuthenticationError:
    ...          # key is missing, malformed or revoked
except errors.APIError as exc:
    print(exc.status_code, exc.code, exc.request_id)

request_id is worth logging. It is the fastest way to get an answer if you need to ask about a specific call.

Timeouts and 5xx responses are retried twice with backoff, and Retry-After is honoured when the server sends it. Set max_retries=0 if you would rather handle that yourself.

Command line

$ pexafy search "morning fog over pine trees" -n 3
0.847  019e0eb8-b028-73cb-9296-dfa70f557bc9   4000x2667   green      Pexels     https://...
0.812  019e4c9b-3022-7660-b43d-e730b8435f24   6000x4000   green      Unsplash   https://...
0.798  019e4f39-66d4-7ef2-bc9b-eb5340fd243e   3648x5472   grey       Pexels     https://...

pexafy photo <id>, pexafy similar <id> and pexafy usage are also there. Add --json to any of them for the raw response.

Attribution

Photos come from several providers with different licence terms. Every photo carries an attribution object with a ready made credit line:

photo.attribution.plain   # Photo by J. Doe
photo.attribution.html    # <a href="...">J. Doe</a>

Check photo.license_type if your use depends on it.

Reference

Method What it does
search(q, **filters) one page of results
iter_search(q, max_results=None, **filters) every page, cursor handled
search_by_image(image, **filters) match an image you supply
get_photo(photo_id) one photo by id
similar(photo_id, **filters) photos close to an existing one
colors() sources() orientations() licenses() filter values you can use
suggest_photographers(q) photographer(username) photographer lookup
collections() create_collection(name) add_to_collection(id, photo_id) saved sets
usage() usage_daily() usage_monthly() usage_by_key() where you are against your quota

Full API documentation is at docs.pexafy.com.

Requirements

Python 3.9 or newer. The only dependency is httpx.

Licence

MIT.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pexafy-0.1.0.tar.gz (15.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pexafy-0.1.0-py3-none-any.whl (13.7 kB view details)

Uploaded Python 3

File details

Details for the file pexafy-0.1.0.tar.gz.

File metadata

  • Download URL: pexafy-0.1.0.tar.gz
  • Upload date:
  • Size: 15.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.3

File hashes

Hashes for pexafy-0.1.0.tar.gz
Algorithm Hash digest
SHA256 0dba1dd7be8ccb00a041215146045ca75edd821c5b555295de227e9f51e2e6a4
MD5 ca38da192efd9204c67a2c36de88078e
BLAKE2b-256 8a2c18f6dd2275c1b45d15e22798a7940fb5b213a83a22460239035fd71b7b31

See more details on using hashes here.

File details

Details for the file pexafy-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: pexafy-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 13.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.3

File hashes

Hashes for pexafy-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3bd86c5a36b60d5b8c3c45a9eec310b625eb08f1c3295cf5549570b037c8feff
MD5 9223d51a1d448d8d28643351d7017c98
BLAKE2b-256 817e823d3f8fd6483f2f98fc9e9c6915dd3e64d1ff1416e84a8edca39f57fc6c

See more details on using hashes here.

Release history Release notifications | RSS feed

0.1.1

2 files

This release

0.1.0 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page