Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124

We are excited to guide you through the process of creating and consuming RESTful services with Joomla’s Web Services API. Whether you are building a mobile app, integrating with a third‑party system, or simply exposing data for a JavaScript front‑end, the API gives you a reliable way to interact with Joomla content. In this article we will walk through the essential steps, from enabling the API to writing custom endpoints, and we will provide practical code snippets that you can copy and adapt for your own projects.
The Joomla API is a set of RESTful endpoints that expose core Joomla resources—such as articles, categories, and users—using the JSON:API specification. It was introduced in Joomla 4 and is built on top of the Joomla MVC framework, which means you can extend it using the same patterns you already know from component development.
Each resource follows the JSON:API structure, with a data object that contains type, id, and attributes. Relationships are expressed through a relationships object, allowing you to retrieve linked data in a single request. This consistency makes it easy to predict how the API will behave across different entities.
Joomla ships the Web Services package as a core extension, but it may be disabled on some installations. To enable it, navigate to Extensions → Manage → Discover, locate “Web Services”, and click “Install”. Once installed, you will see a new menu entry under System → Web Services.
Open System → Web Services → Applications and click “New”. Give the application a name (for example, “Public API”), set the client_id to site, and choose a secret key that will be used for token generation. Save the record, then enable the application by toggling the “Published” switch.
For a quick security checklist, see our guide on Joomla admin security.
Tokens are generated per user and scoped to a specific application. To create a token, go to Users → API Tokens, select a user, and click “Generate”. The resulting string should be stored securely on the client side and sent in the Authorization: Bearer <token> header with each request.
Basic auth is useful for testing. It requires the client to send a base64‑encoded username:password string in the Authorization: Basic <encoded> header. Joomla will validate the credentials against the user table and, if successful, treat the request as an authenticated API call.
For larger integrations you may prefer OAuth 2.0. Joomla’s OAuth server can be installed as an additional extension. Once configured, clients obtain an access token via the standard authorization code flow and then use it exactly like a regular API token.
Articles are accessed through /api/index.php/v1/content/articles. You can filter by category, state, or author using query parameters. For example, ?filter[category]=5 returns all articles that belong to category ID 5.
Categories are available at /api/index.php/v1/content/categories. The endpoint supports hierarchical queries, allowing you to retrieve a tree of sub‑categories with the include=children parameter.
User data can be fetched from /api/index.php/v1/users. When you request a specific user, the response includes relationships to groups and profile fields, which you can expand with include=groups,profile. If you need a refresher on how Joomla organizes users, roles, and groups, see our guide on user management.
Need help installing extensions? Check out our step‑by‑step guide on install Joomla extensions.
# Create a new article (POST)
curl -X POST https://example.com/api/index.php/v1/content/articles \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/vnd.api+json" \
-d '{
"data": {
"type": "articles",
"attributes": {
"title": "New API Article",
"introtext": "This article was created via the API.",
"fulltext": "Full content goes here.",
"state": 1
},
"relationships": {
"category": {
"data": { "type": "categories", "id": "3" }
}
}
}
}'
# Retrieve an article (GET)
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://example.com/api/index.php/v1/content/articles/42
# Update an article (PATCH)
curl -X PATCH https://example.com/api/index.php/v1/content/articles/42 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/vnd.api+json" \
-d '{
"data": {
"type": "articles",
"id": "42",
"attributes": {
"title": "Updated Title"
}
}
}'
# Delete an article (DELETE)
curl -X DELETE https://example.com/api/index.php/v1/content/articles/42 \
-H "Authorization: Bearer YOUR_TOKEN"
'Bearer ' . $token,
'Content-Type' => 'application/vnd.api+json',
'Accept' => 'application/vnd.api+json'
];
// Create a new article
$payload = [
'data' => [
'type' => 'articles',
'attributes' => [
'title' => 'PHP Created Article',
'introtext' => 'Created via PHP Http client.',
'fulltext' => 'Full body text.',
'state' => 1
],
'relationships' => [
'category' => [
'data' => ['type' => 'categories', 'id' => '3']
]
]
]
];
$http = HttpFactory::getHttp();
$response = $http->post($uri->toString(), json_encode($payload), $headers);
if ($response->code === 201) {
$result = json_decode($response->body, true);
echo 'Article created with ID: ' . $result['data']['id'];
} else {
echo 'Error: ' . $response->code . ' – ' . $response->body;
}
?>
Custom routes are registered in the component’s services/provider.php file. Joomla’s service container reads the definitions and maps URLs to controller classes. Below is a minimal example that creates a /api/v1/books endpoint.
set(
'com_mybooks.api',
function (CMSApplication $app) {
$router = $app->get('router');
$router->addRoute(
'api/v1/books',
'MyBooks\\Api\\Controller\\BooksController',
['methods' => ['GET', 'POST', 'PATCH', 'DELETE']]
);
}
);
}
};
?>
The controller extends Joomla\CMS\MVC\Controller\ApiController and implements the standard CRUD methods. Joomla automatically handles request parsing and response formatting when you return a JsonResponse object.
getQuery(true)
->select('*')
->from('#__mybooks')
->order('title ASC');
$db->setQuery($query);
$books = $db->loadObjectList();
return new JsonResponse(['data' => $books]);
}
public function post()
{
$input = $this->input->json->getArray();
$db = Factory::getDbo();
$obj = (object) [
'title' => $input['data']['attributes']['title'],
'author' => $input['data']['attributes']['author'],
'year' => $input['data']['attributes']['year']
];
$db->insertObject('#__mybooks', $obj);
$obj->id = $db->insertid();
return new JsonResponse(['data' => $obj], 201);
}
// Implement patch() and delete() similarly
}
?>
Finally, add the service provider to the component’s composer.json under the extra section so Joomla loads it automatically.
{
"name": "joomla/component-mybooks",
"extra": {
"joomla": {
"serviceProvider": "services/provider.php"
}
}
}
For tips on making your URLs SEO‑friendly, read our article on SEO-friendly URLs.
All responses follow the JSON:API schema. A typical response contains a data array, optional meta information, and a links object for pagination. Here is an example of a paginated list of articles:
{
"data": [
{
"type": "articles",
"id": "12",
"attributes": {
"title": "First article",
"state": 1
},
"relationships": {
"category": {
"data": { "type": "categories", "id": "3" }
}
}
},
{
"type": "articles",
"id": "13",
"attributes": {
"title": "Second article",
"state": 1
}
}
],
"links": {
"self": "https://example.com/api/index.php/v1/content/articles?page[number]=1",
"next": "https://example.com/api/index.php/v1/content/articles?page[number]=2"
},
"meta": {
"page": {
"total": 45,
"size": 20,
"number": 1
}
}
}
Pagination is controlled with the page[number] and page[size] query parameters. Filtering uses the filter[attribute]=value syntax. Joomla automatically validates these parameters and returns appropriate error messages if they are malformed.
The API uses standard HTTP status codes. Below are the most frequent responses you will encounter:
When you build custom endpoints, you can raise a Joomla\CMS\Exception\UnauthorizedException or Joomla\CMS\Exception\InvalidArgumentException. Joomla catches these exceptions and converts them into the appropriate JSON:API error object.
user->authorise('core.create', 'com_mybooks')) {
throw new UnauthorizedException('You do not have permission to create a book.');
}
if (empty($input['data']['attributes']['title'])) {
throw new InvalidArgumentException('Title field is required.');
}
?>
To protect your site from abuse, configure rate limiting in the webservices plugin. You can set a maximum number of requests per minute per token, and Joomla will return a 429 Too Many Requests response when the limit is exceeded.
Never trust client data. Use Joomla’s built‑in validation classes, such as Joomla\CMS\Form\Form, to verify that incoming fields meet the expected format. Strip HTML tags from text fields unless you explicitly allow them.
All API traffic should be encrypted with HTTPS. This prevents token interception and protects user credentials. If you run a multi‑domain setup, ensure that the SSL certificate covers every sub‑domain that serves the API.
Performance tuning is also important. Review our recommendations on performance optimization for tips on caching and database indexing.
Start by creating a new collection and adding an environment variable for the base URL and the bearer token. Each request can reference these variables, making it easy to switch between development and production servers.
// Example Postman environment variables
{
"baseUrl": "https://example.com/api/index.php/v1",
"token": "YOUR_TOKEN"
}
Postman’s test scripts let you assert response status, JSON structure, and specific field values. Below is a simple test that checks whether a newly created article returns a 201 status and includes an id field.
// Postman test script
pm.test("Status code is 201", function () {
pm.response.to.have.status(201);
});
pm.test("Response contains article ID", function () {
var json = pm.response.json();
pm.expect(json.data.id).to.exist;
});
Yes. Because the API follows the JSON:API spec, any HTTP client—whether it’s Guzzle in Laravel, Axios in Vue, or fetch in vanilla JavaScript—can interact with Joomla endpoints as long as you provide the correct authentication headers.
The core Web Services package is bundled with Joomla 4, but you may need to enable it via the Extension Manager. Optional extensions, such as OAuth, can be added if your project requires them.
Define a new API controller that reads and writes to your custom table, then register the route in the component’s service provider. Joomla will handle request parsing and response formatting for you.
Place the version number in the URL (for example, /api/v1/…). When you need to introduce breaking changes, create a new version folder (e.g., /api/v2/…) and keep the old version functional for existing clients.
By default Joomla does not impose a hard limit, but you can enable rate limiting in the Web Services plugin settings. We recommend setting a reasonable threshold based on your traffic patterns.
We have covered the full lifecycle of Joomla API development—from enabling the built‑in Web Services to crafting custom endpoints, handling authentication, and testing with tools like Postman. By following the patterns and security recommendations presented here, you can deliver reliable, fast, and maintainable RESTful services that integrate smoothly with any client application.
Ready to start building? Explore more of our guides and keep your Joomla site secure, fast, and easy to integrate.