The unofficial Python client for Strapi.
Project description
Strapi Python Client
The unofficial Python client library to easily interface with Strapi from your Python project.
Installation
pip install strapi-py
Quick Start
from strapi_client import strapi
# Create a client instance
client = strapi(base_url="http://localhost:1337/api")
# Fetch data from a custom endpoint
response = client.fetch('/articles')
data = response.json()
print(data)
Authentication
API Token Authentication
from strapi_client import strapi
# Initialize the client with yor API token
client = strapi(
base_url="http://localhost:1337/api",
auth="your-api-token-here"
)
# All requests will include the Authorization header
response = client.fetch('/articles')
Custom Headers
client = strapi(
base_url="http://localhost:1337/api",
headers={
"X-Custom-Header": "value",
"Accept-Language": "en"
}
)
Working with Collection Types
Collection types represent multiple entries (e.g., articles, products, users).
Find All Entries
# Get all articles
articles = client.collection('articles')
response = articles.find()
print(response.json())
Find with Filters and Sorting
articles = client.collection('articles')
# Filter and sort
response = articles.find(params={
'filters': {
'title': {
'$contains': 'Python'
},
'publishedAt': {
'$notNull': True
}
},
'sort': ['createdAt:desc'],
'pagination': {
'page': 1,
'pageSize': 10
},
'populate': '*'
})
Find One Entry
articles = client.collection('articles')
# Get article with documentId j964065dnjrdr4u89weh79xl
response = articles.find_one("j964065dnjrdr4u89weh79xl", params={
'populate': ['author', 'comments']
})
print(response.json())
NOTE
Strapi 5 replaces the numeric
idused in Strapi 4 with a new 24-character alphanumeric identifier calleddocumentId. When working with Strapi 5, usedocumentIdas the primary resource identifier. This library continues to support the legacyidfield for Strapi 4 projects.
Create an Entry
articles = client.collection('articles')
response = articles.create(data={
'title': 'My New Article',
'content': 'This is the article content',
'publishedAt': '2024-01-01T00:00:00.000Z'
})
created_article = response.json()
print(f"Created article with ID: {created_article['data']['documentId']}")
Update an Entry
articles = client.collection('articles')
response = articles.update(1, data={
'title': 'Updated Article Title',
'content': 'Updated content'
})
Delete an Entry
articles = client.collection('articles')
response = articles.delete(1)
Working with Single Types
Single types represent a single entry (e.g., homepage, about page, settings).
Find Single Type
homepage = client.single('homepage')
response = homepage.find(params={'populate': '*'})
print(response.json())
Update Single Type
homepage = client.single('homepage')
response = homepage.update(data={
'title': 'Welcome to My Site',
'description': 'A brief description'
})
Delete Single Type
homepage = client.single('homepage')
response = homepage.delete()
Working with Plugins
Users & Permissions Plugin
# Access users endpoint
users = client.collection(
resource='users',
plugin={'name': 'users-permissions', 'prefix': ''}
)
# Create a new user
response = users.create(data={
'username': 'johndoe',
'email': 'john@example.com',
'password': 'SecurePassword123!'
})
Custom Plugin with Prefix
# Access a custom plugin endpoint
blog_posts = client.collection(
resource='posts',
plugin={'name': 'blog', 'prefix': 'blog'}
)
# This will make requests to /blog/posts
response = blog_posts.find()
Custom Plugin without Prefix
# Disable plugin prefix
custom_content = client.collection(
resource='items',
plugin={'name': 'custom-plugin', 'prefix': ''}
)
# This will make requests to /items
File Management
Upload a File
# Upload from bytes
with open('image.jpg', 'rb') as f:
file_data = f.read()
response = client.files.upload(
file_data=file_data,
filename='image.jpg',
mimetype='image/jpeg',
file_info={
'alternativeText': 'A sample image',
'caption': 'My caption'
}
)
print(response.json())
Upload with File Object
import io
# Upload from file-like object
with open('document.pdf', 'rb') as f:
response = client.files.upload(
file_data=f,
filename='document.pdf',
mimetype='application/pdf'
)
List All Files
# Get all files
response = client.files.find()
files = response.json()
# Filter files
response = client.files.find(params={
'filters': {
'mime': {'$contains': 'image'}
},
'sort': 'createdAt:desc'
})
Get a Specific File
response = client.files.find_one(file_id="clkgylmcc000008lcdd868feh")
file_data = response.json()
print(f"File URL: {file_data['url']}")
Update File Metadata
response = client.files.update(
file_id="clkgylmcc000008lcdd868feh",
file_info={
'name': 'renamed-file.jpg',
'alternativeText': 'Updated alt text',
'caption': 'Updated caption'
}
)
Delete a File
response = client.files.delete(file_id="clkgylmcc000008lcdd868feh")
Custom Paths
You can specify custom API paths for content types:
# Use a custom path instead of the default /articles
custom_articles = client.collection(
resource='articles',
path='/v2/custom-articles'
)
response = custom_articles.find()
# Makes request to /v2/custom-articles
Error Handling
The library provides detailed error messages from Strapi, making debugging much easier. When an error occurs, you'll see:
- Error type/name (e.g., ValidationError, ApplicationError)
- Clear error message from Strapi
- Detailed field-level validation errors with paths
- Access to the original response for debugging
Basic Error Handling
from strapi_client import (
strapi,
StrapiHTTPError,
StrapiHTTPNotFoundError,
StrapiHTTPUnauthorizedError,
StrapiHTTPBadRequestError,
StrapiValidationError
)
try:
client = strapi(base_url="http://localhost:1337/api")
articles = client.collection('articles')
response = articles.find_one(999)
except StrapiHTTPNotFoundError as e:
print(f"Article not found: {e}")
print(f"Status code: {e.response.status_code}")
except StrapiHTTPUnauthorizedError as e:
print(f"Authentication failed: {e}")
except StrapiHTTPBadRequestError as e:
print(f"Bad request: {e}")
print(f"Full error: {e.response.json()}")
except StrapiHTTPError as e:
print(f"HTTP error occurred: {e}")
print(f"Response: {e.response.text}")
except StrapiValidationError as e:
print(f"Validation error: {e}")
Detailed Validation Errors
When you have validation errors (e.g., in dynamic zones or complex fields), the error message will show exactly which fields are problematic:
try:
blog = client.collection('blogs')
response = blog.create(data={
'title': '', # Invalid: too short
'blocks': [
{
# Missing __component field
'body': 'Some content'
}
]
})
except StrapiHTTPBadRequestError as e:
print(e)
# Output:
# Strapi API Error (400): [ValidationError] Invalid data provided
# Validation errors:
# - title: title must be at least 1 characters
# - blocks.0.__component: component is required
Accessing Response Details
All HTTP errors preserve the original response, allowing you to access additional details:
try:
response = client.collection('articles').create(data={...})
except StrapiHTTPBadRequestError as e:
# Get status code
print(f"Status: {e.response.status_code}")
# Get full error details
error_details = e.response.json()
print(f"Error name: {error_details['error']['name']}")
print(f"Error details: {error_details['error']['details']}")
# Get request Url
print(f"Request URL: {e.response.request.url}")
Available Error Types
StrapiError- Base error classStrapiValidationError- Invalid input or configurationStrapiHTTPError- Base HTTP error (non-2xx responses)StrapiHTTPBadRequestError- 400 Bad Request (validation errors, malformed requests)StrapiHTTPUnauthorizedError- 401 Unauthorized (authentication required)StrapiHTTPForbiddenError- 403 Forbidden (insufficient permissions)StrapiHTTPNotFoundError- 404 Not Found (resource doesn't exist)StrapiHTTPTimeoutError- 408 Request TimeoutStrapiHTTPInternalServerError- 500 Internal Server Error
Advanced Usage
Locale Support
articles = client.collection('articles')
# Fetch French content
response = articles.find(params={'locale': 'fr'})
# Fetch specific entry in Spanish
response = articles.find_one(1, params={'locale': 'es'})
Population
articles = client.collection('articles')
# Populate all relations
response = articles.find(params={'populate': '*'})
# Populate specific relations
response = articles.find(params={
'populate': ['author', 'categories', 'cover']
})
# Deep population
response = articles.find(params={
'populate': {
'author': {
'populate': ['avatar']
},
'categories': '*'
}
})
Field Selection
articles = client.collection('articles')
# Select specific fields only
response = articles.find(params={
'fields': ['title', 'description', 'publishedAt']
})
Requirements
- Python >= 3.10
- httpx >= 0.27.0
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT License - see LICENSE file for details.
Links
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 strapi_py-0.1.1.tar.gz.
File metadata
- Download URL: strapi_py-0.1.1.tar.gz
- Upload date:
- Size: 17.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
46c21ba374673187dbab530075759603f20fb6b5b065b77bac7387dc4b55da50
|
|
| MD5 |
2e715f4803320aac16b1593490dec2e6
|
|
| BLAKE2b-256 |
6f6e0d894e78718fa0fa72e1ba0ae1a5fc8f363d36e7621b6d2ddad1b73f4f01
|
File details
Details for the file strapi_py-0.1.1-py3-none-any.whl.
File metadata
- Download URL: strapi_py-0.1.1-py3-none-any.whl
- Upload date:
- Size: 25.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a17a93c7e823ae0b67a9553921984ce817097b1d6955c047c768c9534ca20821
|
|
| MD5 |
6af324490eda8da117fe09a2e4a62dbd
|
|
| BLAKE2b-256 |
662a3ad0f6e8df7a55289fe6efcd0f5c14b0358c34d88ba213e0748bdace6bc8
|