Python client for the Unsplash API
Project description
Unsplash Wrapper
Typed async Python client for the Unsplash API focused on the photo search endpoint.
Features:
- Async HTTP via
httpx - Pydantic models (requests & responses)
- Fluent builder (
UnsplashSearchParamsBuilder) OR direct kwargs - Returns a simple
list[UnsplashPhoto] - Automatic retry of 429 (rate limit) responses
Installation
Requires Python 3.13+.
pip install unsplash-wrapper
Set your Unsplash access key (Create one in your Unsplash developer dashboard):
export UNSPLASH_API_KEY=your_access_key_here # macOS/Linux
$env:UNSPLASH_API_KEY='your_access_key_here' # PowerShell
Quick Start (async context manager)
from unsplash_wrapper import UnsplashClient, UnsplashSearchParamsBuilder
params = UnsplashSearchParamsBuilder().query("mountains").build()
async with UnsplashClient() as client:
photos = await client.search_photos(params)
for photo in photos:
print(photo.id, photo.url)
Without Context Manager
UnsplashClient can also be used without async with — it creates and closes an
httpx.AsyncClient per call automatically:
from unsplash_wrapper import UnsplashClient, UnsplashSearchParamsBuilder
params = UnsplashSearchParamsBuilder().query("ocean").build()
client = UnsplashClient()
photos = await client.search_photos(params)
for photo in photos:
print(photo.id, photo.url)
Sync Usage Helper
If you're not already inside an async context:
import asyncio
from unsplash_wrapper import UnsplashClient, UnsplashSearchParamsBuilder
def main() -> None:
params = UnsplashSearchParamsBuilder().query("ocean").build()
photos = asyncio.run(UnsplashClient().search_photos(params))
for p in photos:
print(p.id, p.url)
if __name__ == "__main__":
main()
Search Parameter Builder
All builder methods are chainable. Pass the result of .build() to search_photos.
from unsplash_wrapper import (
UnsplashClient,
UnsplashSearchParamsBuilder,
)
params = (
UnsplashSearchParamsBuilder()
.query("sunset beach")
.limit(20)
.landscape_orientation()
.high_quality()
.order_by_latest()
.page(2)
.build()
)
async with UnsplashClient() as client:
photos = await client.search_photos(params)
for photo in photos:
print(photo.id, photo.description, photo.url)
Available builder methods:
| Method | Description |
|---|---|
.query(str) |
Search query |
.limit(int) |
Results per page (default: 10) |
.page(int) |
Page number (default: 1) |
.orientation(Orientation) |
Set orientation enum directly |
.landscape_orientation() |
Shortcut for landscape |
.portrait_orientation() |
Shortcut for portrait |
.squarish_orientation() |
Shortcut for squarish |
.content_filter(ContentFilter) |
Set filter enum directly |
.high_quality() |
Shortcut for high content filter |
.low_quality() |
Shortcut for low content filter |
.order_by(OrderBy) |
Set order enum directly |
.order_by_relevant() |
Shortcut for relevance ordering |
.order_by_latest() |
Shortcut for latest ordering |
Manual Params
from unsplash_wrapper import UnsplashSearchParams, UnsplashClient, Orientation
params = UnsplashSearchParams(query="minimal", per_page=5, orientation=Orientation.SQUARISH)
async with UnsplashClient() as client:
photos = await client.search_photos(params)
Models Overview
UnsplashPhoto attributes:
id: str
description: str | None
alt_description: str | None
urls: UnsplashUrls (raw/full/regular/small/thumb)
user: UnsplashUser (username, name, portfolio_url, ...)
width, height, color, likes, created_at
url (property -> regular URL string)
Error Handling
Exceptions from unsplash_wrapper.exceptions:
UnsplashAuthenticationException(401)UnsplashNotFoundException(404)UnsplashRateLimitException(429) — includesretry_afterif providedUnsplashServerException(5xx)UnsplashClientException(other 4xx)UnsplashTimeoutException(request timeout)
from unsplash_wrapper import (
UnsplashClient,
UnsplashRateLimitException,
UnsplashAuthenticationException,
)
client = UnsplashClient()
try:
photos = await client.search_photos(query="forest")
except UnsplashRateLimitException as e:
msg = f"retry after {e.retry_after}s" if e.retry_after else "no retry window"
print("Rate limited:", msg)
except UnsplashAuthenticationException:
print("Invalid API key configured")
Retry Behavior
429 responses are retried up to 3 times (delays: 1s → 2s → 4s). Other failures propagate immediately.
Logging
Logger name: unsplash_wrapper.UnsplashClient
import logging
logging.basicConfig(level=logging.INFO)
Development
Run tests:
uv run pytest -q
Type check (if mypy configured):
mypy unsplash_wrapper
Project details
Release history Release notifications | RSS feed
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 unsplash_wrapper-0.3.0.tar.gz.
File metadata
- Download URL: unsplash_wrapper-0.3.0.tar.gz
- Upload date:
- Size: 36.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
373fe2d0b88806c7b3f41507c3604c64d407169a62d3fe27bd18bb5ca02da233
|
|
| MD5 |
abcc2799735348b81b97cf6fc47b3bce
|
|
| BLAKE2b-256 |
5b6936be0319305b8538e5d4ac5da14740b6ef286666632d5f698f1b45663da0
|
File details
Details for the file unsplash_wrapper-0.3.0-py3-none-any.whl.
File metadata
- Download URL: unsplash_wrapper-0.3.0-py3-none-any.whl
- Upload date:
- Size: 8.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
500bacc06260410edf3a79af2d7084f3a4bdb3557d004159e761e12fc34c2bd6
|
|
| MD5 |
4b5c2c74f026c85753d81905cb45ca7b
|
|
| BLAKE2b-256 |
13d7d3a40031a02f1fd2582a14259fdd3b087c237fd5669cf15bf341107ac000
|