Joomla 4 – Playing with the Joomla Web Services (API)

by Marc Dechèvre



Present slides

At the end of this presentation, you will be able a.o. to import your Articles together with their Custom Fields to your Joomla website directly from a Google Sheet thanks to the Joomla API

This presentation was made

1. Please contribute

Help improving this presentation that will be converted to a Joomla Community Magazine article:

then please get back to me! ❤️💙🧡💚

1. How this presentation came to life

There have already been several presentations about the Joomla API.

They were certainly interesting (showing Postman, explaining how you can create a Smartphone App exchanging with your Joomla website, …) but I was always a bit frustrated because :

This changed on 23 September 2022 where I had the chance to be present at JoomlaDay D-A-CH (gathering several german-speaking countries, namely Germany – Austria – Switzerland). In particular Peter Martin gave an excellent “How to use APIs in your Joomla website?” (see links hereafter): At the end of his session, Peter would give a few little PHP scripts showing how to create or update Articles.

I asked to Peter whether he could share his scripts. He did even better: there is a new official website Joomla! Programmers Documentation with a page about Web Services and Peter made a Pull Request on Github to add the examples (see hereafter).

Coming back to Belgium, I was really excited to play with these examples.

Then I contacted Alexandre Elisé from Martinique, who is very passionate about the Joomla API and we elaborated on these examples. Indeed

So after several iterations over a few weeks, we ended up with a series of “little” scripts. The goal was indeed

So a long story short: this presentation aims at allowing a maximum of people to start using the Joomla API, which offers a strong competitive advantage compared to other CMS for example.

2. Resources

Peter Martin – “How to use APIs in your Joomla website?” at JoomlaDay D-A-CH on 2022.11.23

Alexandre Elisé

When you go to please click on the Star button if you appreciate Alexandre’s scripts & contribution : Alexandre

Joomla! Programmers Documentation (in construction)

Joomla! Documentation

3. How to get the Joomla API Token

Before using the Joomla API (and any of the scripts below) we need a Joomla API Token.

At the moment, the Joomla API Token can only be generated for Super Users.

So be sure to keep it secret because any person having somehow access to that Token is de facto a Super User and can do everything on your website, including deleting everything.

Note: as far as I know, it is the idea at some point to allow non Super Users to access the API (which is necessary if you want to give a Token to a third-party who only wants to fetch data for example).

[EDIT] On 2022.11.25 Alexandre shares this additional information on Facebook:
“You don’t need to be a Super User to access the Joomla Api. You can create a Web Services usergroup for example, go to Global Configuration and give this Group Permissions for API and site login to be able to see the token. You need also to add the Web Services group you just created to the list of allowed groups in the token user plugin config. But for now you still cannot have fine-grained ACL and Access Level per endpoint.For more security you could also disable sensitive webservices plugins like the users web services plugin but then the consumer app using the token cannot manage user info. I haven’t tried it yet, but I think if you give specific permissions to the Web Services usergroup then add only app user in this group you could achieve want you want even if it’s still not per route / endpoint.”
“Just did a quick test on a fresh joomla 4.2.5 install you don’t need to be Super User. morever Joomla seems to already handle permissions per endpoint but only for core endpoints. At least that’s what I’ve tried. that means that if you create for example a web services group to manage all consumers of your Joomla web services and then deny create patch delete users for this usergroup the app/consumer of the joomla web services will get a 403 Forbidden http status code when trying to perform denied actions.”
Joomla Api Without Super User > Demo video (18 min)

3.1. Create a new User

Actually you could of course create a Joomla API Token for your own account.

But it is probably a good practice to create a distinct user


3.2. Give a strong password


3.3. Assign to the User Group Super User


3.4. Go the Joomla API Token tab and Read


3.5. Save

Now that you have saved the User, the Joomla API Token has been created… but for security reasons only that given user can see it (nobody else, not even other Super Users).


3.6. Log in as the new User and copy the Token


4. Create, Update, Fetch and Delete Joomla Articles with the API

4.1. Create Joomla Articles with the API

4.1.1. Terminology

Some basic terminology first: When using the Joomla API

4.1.2. See the Articles Manager

This is a fresh website so I see that “No Articles have been created yet”


4.1.3. Prepare the POST script

Create your POST script on your website

Customize your POST script on your website


4.1.4. articletext vs introtext and fulltext

Good to know: when you Create an article

4.1.5. The 4 necessary fields

To create an article

4.1.6. Execute your script



4.1.7. Go back to or refresh the Articles Manager

Tadam you can see that the Article was indeed created!


4.2. Update Joomla Articles with the API

4.2.1. Prepare the PATCH script

The procedure is totally similar to the one of the previous script:

Create your PATCH script on your website

Customize your PATCH script on your website


Good to know: when you Update an article

4.2.2. Execute your script

Simply execute your script as already explained above.


4.2.3. Go back to or refresh the Articles Manager

When opening the Article, we can indeed check that


4.3. Playing with Custom Fields

Adding a value to some Custom Field of an Article directly in the database using a query in PHPMyAdmin is not so obvious because it involves several Tables which are linked.

But as you will see, adding a value to some Custom Field of an Article via the Joomla API is super easy.

4.3.1. Create a Custom Field

In this example I create a Custom Field of Type “Text”


4.3.2. Adapt the PATCH script

Now I need to edit my api-patch script as follows


4.3.3. Execute your script

Simply execute your script as already explained above.

You already see on what is displayed that “temperature” is mentioned.


4.3.4. Go back to or refresh the Articles Manager

When opening the Article, we can indeed check that


4.4. Fetch Joomla Articles with the API

A good usecase for this script: an intranet or an app where one wants to display some news (articles) from your Joomla website

4.4.1. Prepare the GET script

Create your GET script on your website

Customize your GET script on your website


4.4.2. Bonus 1: select another Category

In this example, we suppose we want to get all articles from Category 2

Of course, you can easily change the ID of the Category you need by adapting the following line of code:

$categoryId = 2;

4.4.3. Bonus 2: only get Published Articles

What is you only want to get Articles which are Published.

In the database, you probably know that technically being

So all we need to change in the script is to change




4.4.4. Execute the script

Simply execute your script as already explained above.


4.5. Delete Joomla Articles with the API

Create your DELETE script on your website

Customize your DELETE script on your website

We don’t provide screenshots here. By now you know how it works!

5. How to launch any of the scripts automatically

Given the fact that our scripts here are independent of Joomla

Lauching any of the scripts can be simply done:

So obviously it means that you can automate the execution of the creation / update of your Joomla Articles at any time interval.

5.1. Example 1: via a nice button in the backend

In Joomla 4 it has become even easier than before to create a Custom HTML Module in the backend.

Take advantage of this feature to create a nice interface to your Users.


5.2. Example 2: via Joomla Task Scheduler

The Task Scheduler is a new feature introduced with Joomla 4.


6. Be the only one who can run your scripts

If somebody know that you have a script called for example api-test.php, this person could flood your websites with request on that url.

A typical way to avoir that is on Apache servers is to create a htpsswd, meaning you have to

When a htpsswd is enabled, you get a popup asking for the defined username & password before accessing.

You can typically

But we need to be more specific because

  1. we only want to block our scripts, not our whole website.
  2. we only want to block our scripts if they are launched in the browser, not if they are launched by the website itself
    • either via some code
    • either via the Task Scheduler

Let us first show a good example of such an .htaccess rule:

<FilesMatch "^api*">
AuthType Basic
AuthBasicProvider file
AuthUserFile /YOUR_PATH/public_html/.htpasswd
AuthName secure
Require valid-user
Require ip

7. Change the API Token if needed

If you have any reason to think that your API key is not private any more (like me after having shared this presentation) then simply Edit the User in question, go to the Joomla API Token tab and click on Reset

If a script having a wrong or revoked Token is called then it will display the following error message: “forbidden”


7.1. How did restrict the rule to our scripts

Let’s suppose that all my scripts filenames start with api.

Then I want to have this htpsswd popup asking only for the files starting with api.

This is why in the snippet of .htaccess below we have <FilesMatch "^api*">

7.2. How did we allow the scripts if launched by the website itself

See the line Require ip

7.3. More information

For more information about this kind of .htaccess rules, see

8. Using the Joomla API to Import and Update Articles directly from a Google Sheet


Being able to POST or PATCH a single Article programmatically with a few lines of code thanks to the API as shown above is great… but the added value in real life is quite limited at this point.

Indeed, having to prepare 1 script to create only 1 article would seem quite cumbersome. But once you can do it for 1, one starts to think “what about having a loop to create multiple articles” :)

When I first spoke about this with Alexandre, I would already have been happy to do it with a CSV file:

But Alexandre went further:

8.1. Prepare Google Sheets

8.1.1. Create Google Sheets

Create a Google Sheets

Now we need to have some sample data with the right structure for our Google Sheets. We don’t want to reinvent the wheel so let’s have a look at the following script:

In particular I see the following line $csvUrl = '';

Excellent, let’s copy/paste this URL in a browser

This makes my browser download a CSV file. I could start from this file… but an easier way is to simply display the Google Sheet directly on the browser by dropping the ?output=csv at the end of the URL.

With other words, I open



8.1.2. Share Google Sheets

In Google Sheets

In our case, the url is

So if you want to have a preview of that directly on your browser, take the url without what follows /pub, namely

Screenshots (my computer is obviously is French but it is pretty straigtforward):




8.2. Basic example

8.2.1. Prepare Google Sheets script

Create your Google Sheet script on your website

Customize your Google Sheet script on your website. In your file adapt the following variables


8.2.2. Execute your script

Simply execute your script as already explained above.

Normally this script should not even display anything and just run in the background.

Still the script displays the “raw result”: this is clearly not necessary but allows to visually check that something indeed happened (and once I could see that some rows were not processed and could identify the cause in the Google Sheets for example).


Beware: I have already noted that Google Sheets sometimes take 1 minute or so before actually updating the CSV version (even if other users of the Google Sheet see your changes “live”). So if after executing your script you see no difference, simply wait 1 minute and relaunch it

8.2.3. Go back to or refresh the Articles Manager

When opening the Article Manager, we can indeed check that


And when opening any of them, we can also see that everything is there




8.2.4. If you want to Update your articles later on

You have probably noted that the first column in the Google Sheet was id, being of course the ID for each article.

Actually we wanted one single script which works both to Create and to Update Articles.

But how to make the script “smart” enough for that?

So at this point, if there is a chance that I execute again this script in the future, I don’t want to re-create the already existing articles.

Therefore, in my Google Sheet

Illustration with my adapted Google Sheet where


And the result in the Articles Manager after running the script again:


Obviously in your Google Sheets you can change the value of any field. Example: language is set on * (meaning all languages since it is the way it is encoded in the database) but you could specify a language like fr-FR

8.2.5. Two rounds to process

The script runs two loops :

8.2.6. Other smart features

Note that the script has also been made as “smart” as possible for different aspects. Examples:


8.3. Advanced example

The following steps are not necessary of course: it only applies if you intend to use the script for example

8.3.1. Create Custom Fields in Joomla

Let’s suppose we create a website for a Rock band. It would be much easier for the musicians to add their new concerts simply in a Google Sheets than to lean how to use a CMS.

Those concerts would then be easily imported and/or updated in the website.

So let’s create a new Category Concerts and assign to it the 3 following Custom Fields :




8.3.2. Adapt the Google Sheet

Since we create a new Category (“Concert” with ID 8), let us change the value of the catidcolumn from 2 to 8 in the Google Sheet.

Let us also create the following columns in the Google Sheet:

Note: when you type date, they should have the following format: 2022-11-23 20:00:00 (and visibly it imports dates in the real TimeZone of your website, as if you were typing them from Joomla’s backend)

Here is an example with some values:


8.3.3. Adapt the script

In your script




8.3.4. Go back to or refresh the Articles Manager


Our Categories have indeed been Updated:


Our Custom Fields have indeed been Updated:


Our other native Fields have indeed been Updated:


8.4. Special fields like Images or Custom Fields or Type Subform or URLs

Some special fields entails “multiple values”. So how to handle this?

Suppose you want to import an image or a Custom Field of Type Subform.

As you might be aware, in the database these fields are saved in json format.

So what should we type in our Google Sheet cell ? The easiest way is to save a real Article on the website and see in the database what the corresponding json is.

Note that we don’t need in the Google Sheet to “escape the / character”. With other words we simply type / and not \/

Example for Image (which includes the Intro Image and the Full Image, together with their ALT, Caption etc):

{"image_intro":"images/test.jpg","image_intro_alt":"","float_intro":"","image_intro_caption":"","image_fulltext":"images/test.jpg","image_fulltext_alt":"","float_fulltext":"", "image_fulltext_caption":""}

Example for a Custom Field of Type Subform having in this case 2 Custom Fields (field2 and field7) and 3 values (row0, row1, row2) – note: it also accepts HTML tags but I can’t paste them here in the presentation:

{"row0":{"field2":1410,"field1":"This an article summary 1 for this dessert. Short & Sweet!", “field7”: “What’s up Super Joomlers! Alex here…Proud to be a joomler. Nowadays focusing on Joomla! 4.x Web Services Apis.”}, “row1”:{"field2":1410,"field1":"This an article summary 2 for this dessert. Short & Sweet!", “field7”: “What’s up Super Joomlers! Alex here… Proud to be a joomler. Nowadays focusing on Joomla! 4.x Web Services Apis.”}, “row2”:{"field2":1410,"field1":"This an article summary 3 for this dessert. Short & Sweet! “, ”field7": “What’s up Super Joomlers! Alex here… Proud to be a joomler. Nowadays focusing on Joomla! 4.x Web Services Apis.”}}

Example for the urls Field (those URL A, URL B and URL C which are native in Joomla):

{"urla":"","urlatext":"Website","targeta":"","urlb":"", "urlbtext":"Github “,”targetb":"","urlc":"","urlctext":"Twitter", "targetc":""}

Note: my Editor is tweaking my code snippets here. Of course all the " are regular double quotes (and not italic). And the Github icon is also added by the Editor

8.5. Fields with potentially multiple values like Custom Fields of Type List or Checkbox

Suppose you have a Custom Field of Type List or Checkbox. For those CF

In that case

8.6. Fields with potentially multiple values like Custom Field of Type Articles Field by Regular Labs

Articles Fields by Regular Labs is a powerful Type of Custom Field allowing to select other Articles:

A good example : for a Library website you have typically have a Category “Books” and a Category “Authors”. You could then link each Book Article to one or several Author Article(s).

If you have multiple values, I have tested succesfully the following 2 methods (let’s suppose we want to link to Articles having ID 12 and 13):

  1. in the Google Sheet, type 12,13 then Synchronize. After that Edit the Article in the Backend (the 2 values do appear there but are not yet correctly saved in the database) and Save it
  2. in the Google Sheet, type directly the values in JSON-format, ie {"0":"12", "1":"13"} (without spaces), then Synchronise

8.7. Formula in Google Sheet to transform comma-separated values into the desired JSON

[ added on 30.03.2023 ]

Suppose you have multiple values on the Google Sheet in the form of comma-separated values 12,Se,Lan, Dis.

In order to import them we need to transform them in JSON-format as explained above: {"0":"12", "1":"Se", "2":"Lan", "3":"Dis"} (without spaces)

Let’s see how to achieve this in a few steps, with a Custom Formula.

8.7.1. Apps Scripts

In Google Sheets, go to Extensions > Apps Scripts

Note: if you get a 404 error after a message “too many directions”, it is because Google does not properly manage its redirections when you are logged in simultanously to several Google Accounts. In that case, two options:

In the Apps Scripts interface, paste the following function

 * Transforms a comma-separated list into indexed JSON in order to import multiple values of a Custom Fields into the website
function CREATEJSON(input) {
  if (input) {
    let result = {};
    let key = 0;
    .forEach(item => {
      let val = item;
      result[key] = val;
      key = key + 1;
    }); // we now have an array which is formatted as we wish
    let myjson = JSON.stringify(result, null, ' '); // we transform the array in JSON format
    myjson = myjson.replace(/\n/g,""); // we remove the line breaks \n globally g
    myjson = myjson.replaceAll(" ",""); // we remove all spaces
    return myjson;

What does this script do?

8.7.2. Formula in your Google Cell

In your Google Sheet you can now type the following : CREATEJSON(A2) where A2 is your source cell with 12,Se,Lan, Dis for example. Then you will see the JSON exactly as needed, ie {"0":"12", "1":"Se", "2":"Lan", "3":"Dis"}.

8.8. Performance

When I first started to play with this script I was importing 100 to 200 articles and it was going fine.

Then I decided to raise the bar for testing purposes and tried with some 2600 article.

I think that the script managed to create some 500 articles (having each 10 Custom Fields). Indeed after a few minutes the script was throwing the following Request Timeout :

This request takes too long to process, it is timed out by the server. If it should not be timed out, please contact administrator of this web site to increase 'Connection Timeout'.

So on my hosting I increased the following:

This seems to have done the trick for me since now

Actually, it takes approximately 13 minutes to get all my 2600 articles updated (meaning an average of 200 articles per minute).

If I needed to import even more article, I guess that the following actions would help:

Indeed, every Article creation or update triggers the Smart Search and the Versioning which probably take extra resources before passing onto the next Article.

[ added on 30.03.2023, improved on 2023.04.25 ]

In the latest version of the script released end of March, a new variable $silent has been added. If set to 0 then the script will not display anything on the screen, which can only increase performance. Of course, if you are still testing your customization of your script, you probably want to set it on 1 or 2so that you have some feedback about what has happened.

Illustration below:

// 0: hide both response result and key value pairs (gives a blank screen)

// 1: show response result only (gives a summary for each processed row) silent-1

// 2: show key value pairs only (gives for each row the details of all the processed columns, so custom fields included) silent-2

8.9. Difference between using the API or using a direct SQL query in the database

One could think of importing / updating their Articles directly with a SQL query (based somehow on the same data in the Google Sheet).

Doing so would indeed directly modify the database… but it would not trigger all the Actions foreseen by Joomla (typically onContentAfterSave) such as

So that if those Actions make sense for you then the API is better because it is really as if every Article would be manually edited in the backend.

But for pure performance, writing directly to the database can only be more performant.

9. Troubleshooting

If no Article is created or updated when you launch the script, here is what you can do

  1. check that everything is OK in the script (your API key, the link to the Google Sheet, the structure of the Google Sheet)
  2. check the log:
    • go to System > Global Configuration > tab Logging > enable Log Almost Everything + be sure that Log Priorities is set to all
    • relaunch the script
    • see /administrator/logs/everything.php

Note: if you want to make your life easier, install this little free plugin made by Yannick Gaultier, which allows to view/download/delete all logs files produced by Joomla directly from the backend:

Here are two practical examples of issues that I faced myself.

9.1. Example 1 – some custom field is required

One day I encountered the following error message in /administrator/logs/everything.php

Fields: datetime priority clientip category message 2023-01-18T16:11:36+00:00 CRITICAL error Uncaught Throwable of type Tobscure\JsonApi\Exception\InvalidParameterException thrown with message "Field required: Event Category". Stack trace: #0 [ROOT]/libraries/src/MVC/Controller/ApiController.php(362): Joomla\CMS\MVC\Controller\ApiController->save()

Actually the Custom Field “Event Category” was required but that Custom Field was empty in my Google Sheet.

So just like you could not Save an Article via the Interface in that case, you can’t do it neither via the API…

9.2. Example 2 – conflict caused by 3rd party extension

I have already had the case that an extension (CFI plugin in my case) would prevent the API from working, which I also discovered thanks to the Log file.

My workaround was to restrict the plugin to Super Users so that it would not interfere.

9.3. Example 3 – special characters in the Google Sheet

The following one was quite tricky: somes Articles would be imported normally with the script… but some other ones would throw such an error :

Fatal error: Uncaught ValueError: array_combine(): Argument #1 ($keys) and argument #2 ($values) must have the same number of elements in /home/my-sync-file.php:151 Stack trace: #0 /home/my-sync-file.php(151): array_combine(Array, Array) #1 /home/my-sync-file.php(194): {closure}('https://docs.go...', Array, Object(Closure)) #2 {main} thrown in /home/my-sync-file.php on line 151

After some time, I realized that the users were using a kind of “curly single quote” instead of the “regular single quote”… and that “curly single quote” would be interpreted as the end of the field, causing a discrepancy :

l’oasis instead of l'oasis

The only solution to this is to make a Search a Replace for the whole Google Sheet

9.4. Example 4 – Break Line in the Google Sheet

When you type text in some cell of a Google Sheet, if you want to create a new line within that given cell, you can simply press CTRL+ENTER.

Apparently (I should test further to be 100% sure), having this at the end of a Field like Title which is pure text would cause the import/synchronization of the corresponding Article to fail.

For Custom Fields of Type Editor, this would be no problem – I have tested it – although you would loose the “break line” in Joomla, having then one unique long paragraph in the Article (want a solution for that? see the tip in the next section).

10. Final tip

One final tip if you want to have multiple paragraphs in the cells of your Google Sheet :

If you want to replace automatically all the “Break Lines” that users have typed in the cells of your Google Sheet by proper HTML tags like <p> and </p>, you can simply use a Regular Expression (REGEX) like the following one, the “Bread Line” being identified by \n :


11. Using the Joomla API to Import and Update Articles directly from AirTable


You have probably already heard of AirTable:

If not, let’s simplify it by saying that it is a big like a Google Sheet but really more powerful:

In practice, more and more companies/associations are moving their data to Airtable so that they are interconnected and no more in silos.


11.1. Prepare Airtable

After creating a table in Airtable and before using it in our script, we need to make that table publicly available with the Airtable API

Details coming soon

11.2. Prepare your Airtable script


Breaking news: now support for adding multiple downloaded and cached images as attachments

Details coming soon

11.3. –

Details coming soon

12. GraphQL API together with the Joomla API

Add or Edit Joomla! Articles Via API To Multiple Sites Chosen Randomly in from a predefined list Using Github GraphQL API

12.1. Prepare your OMDb script


Details coming soon

12.2. –

Details coming soon

13. OMDb API together with the Joomla API

Note: OMDb is a Movie database

Turn your top 5 favorite movies into Joomla Articles thanks to OMDb API and Joomla API

Details coming soon

13.1. Prepare your OMDb script


Details coming soon

13.2. –

Details coming soon

14. Using the Joomla API with Convert Forms by Tassos

Convert Forms 4 (released around end of February 2023) ships with new very interesting features like Tasks, including Webhooks.

It is nice because it allows for example visitors to create an article with a Form which is simpler than the native Joomla front-end article creation (which shows many fields on many tabs, even if you could always create your own override of edit.php in order to customize it).

14.1. Small introduction as published by Tassos

One of the Apps included in the Convert Forms 4 Tasks new section is Webhooks.

A webhook allows you to automatically send real-time information from your form to some other service or tool outside of your Joomla site without writing a single line of code.

The documentation of the Webhooks App is now published, showing you how to create webhooks with Convert Forms. You can also find a detailed tutorial on utilizing Joomla’s 4 Web Services API and creating a new Article on a remote Joomla 4 site with the power of webhooks!

14.2. My first experience

After creating easily a simple Article with Title & Text thanks to Convert Forms 4, I decided to try to go one step beyond and add Custom Fields:

15. Other ways of importing content into Joomla Articles

The goal of this presentation was to show how to import content into your Joomla Articles with a simple & short independent script, using the Joomla API.

But of course, you might prefer to choose a ready-to-use extension. I surely don’t know all of them, but if I were the first 3 coming to my mind :

16. Thank you

Peter Martin for his excellent presentation about the Joomla API which allowed me to finally dive into this topic

Alexandre Elisé for all the interesting chats about the Joomla API a.o. and for taking the time to provide a new script every time I was having a new idea of improvement!

Tim Davis for having me today on his YouTube Channel

So many members of the Joomla Community for being #jPositive


17. Get in touch

Any suggestion about this presentation?
Please feel free to contact me. I’ll be happy to keep improving it 🙂

Marc Dechèvre | woluweb
+32 474 37 13 12 | +32 2 772 58 69