API
PrestaBlog provides a REST API that allows external tools - scripts, third-party software, or AI agents - to create and manage blog articles and categories without using the back office. This feature, available starting from PrestaShop 8, is designed both for non-developers via an AI agent, and for developers who want a direct integration.
API configuration
First, the API must be enabled and configured in the back office.
Open the configuration:
Modules > PrestaBlog > Configuration > Tools > API
Available settings:
- Enable API: main toggle. The API will not respond to any request while disabled.
- API key: click “Generate a new key”. The key is displayed only once - copy it immediately, it will not be accessible again. Only its cryptographic fingerprint (SHA-256) is stored. If you lose the key, generate a new one.
- Automatically enable new articles: decides whether articles created via the API are published immediately or saved as drafts when the request does not explicitly provide this parameter.
- Rate limit: maximum number of requests per minute per IP address (default: 60).
- Image security: blocks image downloads from private/internal IP ranges. Recommended for installations exposed to the Internet.
- Behind a proxy: enable if your shop is behind Cloudflare, a reverse proxy, or a CDN, to handle client IP correctly.
- Log level: None disables logs. Security logs failed auth attempts and blocks. Debug logs all requests (use temporarily for diagnosis).
- Allowed CORS origins: only if a web app hosted on another domain must call the API from a browser. Leave empty in most cases.
API permissions
The API permissions section lets you restrict precisely what the API key is allowed to do. Each operation can be enabled or disabled independently, both for articles and categories.
- Allow creating articles / categories: allows
POSTrequests to create new content. - Allow updating articles / categories: allows
PUTrequests to update existing content. - Allow deleting articles / categories: allows
DELETErequests. Disable this if you want to prevent accidental deletions from an AI agent or a third-party script.
The API requires HTTPS. Over HTTP, all requests are rejected (except from localhost for local development tests).
Never share your API key.
Use via an AI agent (recommended)
You are not a developer, or you don't want to write code? The easiest method is to use an AI agent like Claude through the MCP protocol (Model Context Protocol). The agent knows exactly which calls to make, handles authentication, and understands your instructions in natural language.
Note: Claude and Windsurf are examples of MCP integrations. There are many other compatible software tools and environments, but it is not possible to review all of them here.
1. Enable the API and generate your key
In the PrestaBlog back office: Tools > API. Enable the API, click “Generate a new key”, and copy the displayed key - it will not be accessible again.
2. Build your MCP connection URL
The connection URL follows this format:
https://prestablog-mcp.prestablog.workers.dev/mcp?key=YOUR_KEY&shop=https://your-shop.com
Replace YOUR_KEY with the key you generated and https://your-shop.com with your shop URL.
Do not confuse this with the “API URL”, which is a different URL used for direct calls.
The URL contains your API key. Treat it like a password: do not share it and do not publish it.
3. Set up with Claude
In Claude, click Customize at the top left, then Connectors,
at the top of the column that appears click the + button, then Add a custom connector.
Fill in:
- Name: anything you want (e.g. PrestaBlog API)
- URL: the MCP connection URL you built in step 2
Confirm. Claude can now access your blog and can create, edit, and delete your articles and categories directly.
Examples of what you can ask Claude:
- “Create an article about summer shoes in the Fashion category”
- “List the 10 latest published articles”
- “Change the title of article 42”
- “Create a new category called News with this description”
- “Delete category 5”
Built-in documentation
Full documentation is built into the MCP connector. Claude reads it automatically, there is nothing else to configure.
4. Set up with Windsurf
In Windsurf, open the Cascade panel, click ... in the top right corner of the panel, then click the MCPs icon (the last icon at the bottom right of the menu). An editor window opens. Add the following configuration after generating the URL correctly (see step 2):
{
"mcpServers": {
"prestablog": {
"serverUrl": "https://prestablog-mcp.prestablog.workers.dev/mcp?key=YOUR_KEY&shop=https://your-shop.com"
}
}
}
Save. Cascade displays 1 MCP at the bottom of the panel, confirming the connection is active.
Examples of what you can ask Cascade:
- “Create an article about summer shoes in the Fashion category”
- “List the 10 latest published articles”
- “Change the title of article 42”
- “Create a new category called News with this description”
- “Delete category 5”
Built-in documentation
Full documentation is built into the MCP connector. Cascade reads it automatically, there is nothing else to configure.
Authentication (REST)
Each request must include the API key in an HTTP header. Two formats are supported:
Authorization: Bearer YOUR_API_KEY
or
X-Api-Key: YOUR_API_KEY
Best practice
The key should never be sent as a URL parameter, to avoid it appearing in server logs.
Automatic blocking: after 5 failed authentication attempts from the same IP, it is blocked for 1 hour. This delay is configurable in the API settings.
Articles
List articles
GET /prestablog-api/articles
Returns the paginated list of articles.
Available parameters: lang (language ID), page,
per_page (max 100), active (1 = published only),
category (category ID), search (search in title),
sort (date, title, id), order (asc, desc).
curl -X GET "https://your-shop.com/prestablog-api/articles?lang=1&page=1&per_page=10" \
-H "Authorization: Bearer YOUR_KEY"
Article details
GET /prestablog-api/articles/{id}
Returns all data for an article: multilingual fields, categories, associated products, related articles, table of contents.
curl -X GET "https://your-shop.com/prestablog-api/articles/18" \
-H "Authorization: Bearer YOUR_KEY"
Create an article
POST /prestablog-api/articles
Minimal example:
curl -X POST "https://your-shop.com/prestablog-api/articles" \
-H "Authorization: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"languages": {
"1": {
"title": "My article",
"content": "<h2>Introduction</h2><p>Article content.</p>"
}
},
"categories": [1],
"active": true
}'
Full example with image, multilingual content, and metadata:
{
"languages": {
"1": {
"title": "My article",
"introduction": "Short text displayed in listings",
"content": "<h2>Section</h2><p>Full HTML content.</p>",
"meta_title": "My article - SEO Title",
"meta_description": "Search engine description",
"meta_keywords": "word1, word2",
"link_rewrite": "my-article"
},
"2": {
"title": "My article",
"introduction": "Short text displayed in listings",
"content": "<h2>Section</h2><p>Full HTML content.</p>"
}
},
"categories": [2, 5],
"active": true,
"date": "2026-06-15 09:00:00",
"enable_toc": true,
"author_id": 1,
"image": {
"url": "https://example.com/image.jpg"
}
}
Available fields:
| Field | Type | Description |
|---|---|---|
languages | object | Content per language, indexed by PrestaShop language ID. Required on creation |
languages.{id}.title | text | Article title. Required for each language |
languages.{id}.introduction | HTML | Short text displayed in listings and at the top of the article |
languages.{id}.content | HTML | Article body |
languages.{id}.meta_title | text | SEO title tag (max 500 chars) |
languages.{id}.meta_description | text | Meta description (max 500 chars) |
languages.{id}.meta_keywords | text | SEO keywords |
languages.{id}.link_rewrite | text | URL slug. Automatically generated from the title if missing |
categories | array of IDs | PrestaBlog categories to associate |
products | array of IDs | Associated PrestaShop products |
related_articles | array of IDs | Related articles |
active | boolean | Immediate publish. If absent, uses the “Automatically enable” setting |
date | datetime | Publication date in YYYY-MM-DD HH:MM:SS. A future date schedules publication |
enable_toc | boolean | Enables table of contents generated from h2/h3 tags in content |
author_id | integer | PrestaBlog author ID |
image.url | URL | Main image to download from a remote URL |
id_shop | integer | Shop ID (multishop only). Default: current shop |
Update an article
PUT /prestablog-api/articles/{id}
Partial update: only fields present in the request are updated. Fields not provided keep their current value. For languages, only those included in the payload are updated.
curl -X PUT "https://your-shop.com/prestablog-api/articles/18" \
-H "Authorization: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"active": false,
"languages": {
"1": {
"title": "New title"
}
}
}'
Delete an article
DELETE /prestablog-api/articles/{id}
Deletes the article and all related data: images (all variants), categories, linked products, related articles.
curl -X DELETE "https://your-shop.com/prestablog-api/articles/18" \
-H "Authorization: Bearer YOUR_KEY"
Article image
POST /prestablog-api/articles/{id}/image
Three methods are available depending on your context:
Remote URL (image hosted online):
curl -X POST "https://your-shop.com/prestablog-api/articles/18/image" \
-H "Authorization: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/image.jpg"}'
Local file (multipart upload):
curl -X POST "https://your-shop.com/prestablog-api/articles/18/image" \
-H "Authorization: Bearer YOUR_KEY" \
-F "file=@/path/to/image.jpg"
Base64 (fallback):
Note: base64 is very large and can become difficult to handle for complex images. When possible, prefer uploading via remote URL.
curl -X POST "https://your-shop.com/prestablog-api/articles/18/image" \
-H "Authorization: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"base64": "/9j/4AAQSkZJRg...", "filename": "photo.jpg"}'
Via MCP (Claude)
Only URL and base64 methods are available via MCP. To send a local file via Claude, encode it to base64 first.
Categories
The API allows you to create, view, update, and delete categories, just like for articles.
List categories
GET /prestablog-api/categories
Returns categories as a tree by default.
Use flat=1 for a flat list,
active=1 for active only,
lang for the language.
curl -X GET "https://your-shop.com/prestablog-api/categories?lang=1" \
-H "Authorization: Bearer YOUR_KEY"
Category details
GET /prestablog-api/categories/{id}
Returns all category data: multilingual fields, article count, associated customer groups.
Create a category
POST /prestablog-api/categories
Minimal example:
curl -X POST "https://your-shop.com/prestablog-api/categories" \
-H "Authorization: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"languages": {
"1": {
"title": "News"
}
},
"active": true
}'
Full example:
{
"languages": {
"1": {
"title": "News",
"description": "<p>All news from our shop.</p>",
"meta_title": "News - SEO Title",
"meta_description": "SEO description of the category",
"meta_keywords": "news",
"link_rewrite": "news"
},
"2": {
"title": "News",
"description": "<p>All the latest news from our shop.</p>",
"link_rewrite": "news"
}
},
"parent": 0,
"position": 1,
"active": true,
"image": {
"url": "https://example.com/category.jpg"
}
}
Available fields:
| Field | Type | Description |
|---|---|---|
languages | object | Content per language. Required on creation |
languages.{id}.title | text | Category title. Required for each language |
languages.{id}.description | HTML | Category description |
languages.{id}.meta_title | text | SEO title tag (max 500 chars) |
languages.{id}.meta_description | text | Meta description (max 500 chars) |
languages.{id}.meta_keywords | text | SEO keywords |
languages.{id}.link_rewrite | text | URL slug. Automatically generated from the title if missing |
parent | integer | Parent category ID. 0 = root |
position | integer | Display order |
active | boolean | Category visible on the front |
image.url | URL | Category image to download from a remote URL |
Update a category
PUT /prestablog-api/categories/{id}
Partial update: only fields present in the request are updated.
curl -X PUT "https://your-shop.com/prestablog-api/categories/1" \
-H "Authorization: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"languages": {
"1": {
"title": "New category name",
"description": "<p>New description.</p>"
}
}
}'
Delete a category
DELETE /prestablog-api/categories/{id}
Deletes the category and all related data: image, links with articles, customer groups.
Articles associated with this category are not deleted, but they lose this category link.
curl -X DELETE "https://your-shop.com/prestablog-api/categories/1" \
-H "Authorization: Bearer YOUR_KEY"
Category image
POST /prestablog-api/categories/{id}/image
Same three methods as for articles: remote URL, local file (multipart), or base64.
Note: base64 is very large and can become difficult to handle for complex images. When possible, prefer uploading via remote URL.
curl -X POST "https://your-shop.com/prestablog-api/categories/1/image" \
-H "Authorization: Bearer YOUR_KEY" \
-F "file=@/path/to/category.jpg"
Authors
Authors are read-only via the API. They must be created in the PrestaBlog back office.
GET /prestablog-api/authors- list all authorsGET /prestablog-api/authors/{id}- author details with multilingual bios
curl -X GET "https://your-shop.com/prestablog-api/authors?lang=1" \
-H "Authorization: Bearer YOUR_KEY"
Error codes
All error responses follow this format:
{
"success": false,
"error": {
"code": "validation_error",
"message": "Human readable error message",
"details": { "field": "languages.1.title" }
}
}
| Code | HTTP | Meaning |
|---|---|---|
unauthorized | 401 | Missing or invalid API key |
api_disabled | 503 | API disabled in configuration |
https_required | 426 | Request must be made over HTTPS |
rate_limited | 429 | Rate limit exceeded, or IP blocked after failed authentication |
invalid_json | 400 | Invalid or missing JSON body |
validation_error | 422 | Invalid field value (details in error.details) |
conflict | 409 | Slug conflict: an article or category with this link already exists |
not_found | 404 | Resource not found |
method_not_allowed | 405 | HTTP method not supported for this endpoint |
payload_too_large | 413 | Request body too large (limit: 10 MB) |
internal_error | 500 | Internal server error |
Rate limit: when exceeded, the 429 response includes a
Retry-After header indicating how many seconds to wait before retrying.
Each response also includes X-RateLimit-Limit and X-RateLimit-Remaining headers.
Python integration
For Python developers, the requests library makes it easy to interact with the API in just a few lines.
pip install requests
import requests
BASE = "https://your-shop.com/prestablog-api"
KEY = "YOUR_KEY"
headers = {"Authorization": f"Bearer {KEY}"}
# List articles
r = requests.get(f"{BASE}/articles", headers=headers, params={"lang": 1, "per_page": 10})
print(r.json())
# Create an article
r = requests.post(f"{BASE}/articles", headers=headers, json={
"languages": {"1": {"title": "My article", "content": "<p>Content</p>"}},
"categories": [1],
"active": True
})
print(r.json())
# Upload an image from a local file
with open("image.jpg", "rb") as f:
r = requests.post(f"{BASE}/articles/18/image", headers=headers, files={"file": f})
print(r.json())