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")
orientation, source and license_type take several values, as a list or as a comma
separated string. color_name takes one: the API filters on a single colour, and passing
more raises rather than quietly filtering on whichever one arrived last.
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pexafy-0.1.1.tar.gz.
File metadata
- Download URL: pexafy-0.1.1.tar.gz
- Upload date:
- Size: 16.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e37fbaedfbd69821db6b5225e6ff8f2dcaf98c5975ed210f4ab8c192ceff56cf
|
|
| MD5 |
1a78a4dd81e79d296d307b47986d45f0
|
|
| BLAKE2b-256 |
4aff50b3498e5cbfed0c07b4866d68fa2cb44eb948f542846740f42fceb0f2f7
|
File details
Details for the file pexafy-0.1.1-py3-none-any.whl.
File metadata
- Download URL: pexafy-0.1.1-py3-none-any.whl
- Upload date:
- Size: 14.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
116582cbda6b076895405944c1aa58515a2c1d5ef241f0d879cf24c5a5551ee9
|
|
| MD5 |
24dd780cfe66f8bd17fa1c6f4d5c9702
|
|
| BLAKE2b-256 |
8d31fa14aad37082a1a9c53eb6df45a3f45190d3d6f9e542236dadfdc4f3f260
|