Skip to content

A Python library to easily interface with Strapi from your Python project.

License

Notifications You must be signed in to change notification settings

Akinwalee/strapi-python

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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.

Links

About

A Python library to easily interface with Strapi from your Python project.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages