Skip to main content

Strapi Python Client

PyPI version Python License: MIT

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 id used in Strapi 4 with a new 24-character alphanumeric identifier called documentId. When working with Strapi 5, use documentId as the primary resource identifier. This library continues to support the legacy id field 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 class
  • StrapiValidationError - Invalid input or configuration
  • StrapiHTTPError - 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 Timeout
  • StrapiHTTPInternalServerError - 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.

Metadata

Release files for strapi-py 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for strapi-py 0.1.1
File Size Uploaded
strapi_py-0.1.1.tar.gz 17.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for strapi-py 0.1.1
File Interpreter ABI Platform
strapi_py-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 42.6 kB

Release files / strapi_py-0.1.1.tar.gz

Download URL strapi_py-0.1.1.tar.gz
Size 17.6 kB
Tags Source
SHA-256 checksum
How to use checksums
46c21ba374673187dbab530075759603f20fb6b5b065b77bac7387dc4b55da50
BLAKE2b-256 checksum
How to use checksums
6f6e0d894e78718fa0fa72e1ba0ae1a5fc8f363d36e7621b6d2ddad1b73f4f01
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.0

Release files / strapi_py-0.1.1-py3-none-any.whl

Download URL strapi_py-0.1.1-py3-none-any.whl
Size 25.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a17a93c7e823ae0b67a9553921984ce817097b1d6955c047c768c9534ca20821
BLAKE2b-256 checksum
How to use checksums
662a3ad0f6e8df7a55289fe6efcd0f5c14b0358c34d88ba213e0748bdace6bc8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.0

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page