Joomla API development RESTful services

Joomla API Development: Building RESTful Services

Marco Vasquez
Written By Marco Vasquez
Marcus Chen
Reviewed By Marcus Chen
Last Updated April 22, 2026

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.

Understanding Joomla’s Web Services API

What is the Joomla API?

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.

Core resources and JSON:API

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.

Enabling and Configuring the API Application

Installing the Web Services package

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.

Setting up the API application in the backend

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.

Authentication Methods

API tokens

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 authentication

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.

OAuth (optional)

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.

Working with Core Endpoints

Articles

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

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.

Users

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.

CRUD Operations – Practical Code Samples

cURL examples

# 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"

PHP examples using Joomla’s Http library

 '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;
}
?>

Building Custom RESTful Endpoints in Your Component

Defining a new API route

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']]
                );
            }
        );
    }
};
?>

Creating a controller class

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
}
?>

Registering the service

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.

Request and Response Formats

JSON:API structure

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
    }
  }
}

Handling pagination and filtering

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.

Error Handling and HTTP Status Codes

Common error responses

The API uses standard HTTP status codes. Below are the most frequent responses you will encounter:

  • 200 OK – successful GET request
  • 201 Created – successful POST request
  • 400 Bad Request – malformed JSON or missing required fields
  • 401 Unauthorized – missing or invalid authentication token
  • 403 Forbidden – user does not have permission for the requested operation
  • 404 Not Found – resource does not exist
  • 422 Unprocessable Entity – validation errors on input data

Throwing exceptions in Joomla

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.');
}
?>

Security Best Practices

Rate limiting

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.

Input validation and sanitization

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.

Using HTTPS

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.

Testing Your API with Postman and cURL

Setting up a Postman collection

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"
}

Automating tests

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;
});

Frequently Asked Questions

Can I use the Joomla API with external frameworks like Laravel?

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.

Do I need to install additional extensions to enable the API?

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.

How do I expose custom tables through the API?

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.

What is the best way to version my API?

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.

Is there a limit on the number of requests per token?

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.

Conclusion

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.

Marco Vasquez
Written By

Marco Vasquez

Developer Relations

Marco is a full-stack developer and Joomla contributor with deep expertise in template development, module creation, and Joomla 5 architecture. He translates complex technical concepts into clear, actionable tutorials that developers at every level can follow.

Last Updated: April 22, 2026
🇬🇧 English | 🇸🇪 Svenska | 🇫🇮 Suomi | 🇫🇷 Français