Confluence REST API for reading and updating wiki pages

In previous posts I wrote how to automate the work with Atlassian Jira, including automated ticket labeling. Now let’s try to use REST API of another popular Atlassian product – Confluence wiki engine.

Confluence REST API

What you may want to automate in Confluence? Obviously, it may be useful to read the pages that your colleagues regularly update and then use this data in some scripts as an input. You may also want to update your own Confluence pages, for example to post Vulnerability Scanning results. πŸ˜‰

Reading pages

You can read the page by Id using this function:

import json

def get_page_json(page_id, expand = False):
    if expand:
        suffix = "?expand=" + expand 
        suffix = ""

    url="" + page_id + suffix
    response = requests.get(url, auth=(user, password))
    response.encoding = "utf8"
    return json.loads(response.text)
print(get_page_json("287346883", ""))


{u'status': u'current', u'body': {u'_expandable': {u'export_view': u'', u'styled_view': u'', u'editor': u'', u'anonymous_export_view': u'', u'view': u''}, u'storage': {u'_expandable': {u'content': u'/rest/api/content/287346883'}, u'representation': u'storage', u'value': u'<p>Test content</p>'}}, u'title': u'Test page', u'_expandable': {u'operations': u'', u'ancestors': u'', u'container': u'/rest/api/space/INFO', u'descendants': u'/rest/api/content/287346883/descendant', u'space': u'/rest/api/space/INFO', u'version': u'', u'metadata': u'', u'children': u'/rest/api/content/287346883/child', u'history': u'/rest/api/content/287346883/history'}, u'extensions': {u'position': u'none'}, u'_links': {u'self': u'', u'collection': u'/rest/api/content', u'tinyui': u'/x/-QWIE', u'base': u'', u'webui': u'/display/INFO/Test+page', u'context': u''}, u'type': u'page', u'id': u'287346883'}

Without “?” Confluence will return detailed information about the version, but will not return the full content of the page toΒ body -> storage -> value. πŸ˜‰

As you can see, JSON data is very convenient. You can easily read the title and html content:



Test page
<p>Test content</p>


OK. How can we change the content of this page? We need to send some minimal JSON to the Confluence API, for example using this function:

def set_page_json(page_id,json_content):
    headers = {
        'Content-Type': 'application/json',

    response = requests.put("" + page_id, headers=headers, data=json.dumps(json_content),
                 auth=(user, password))

What should be the structure of this JSON?

I cook it this way:

json_data = functions_confluence.get_page_json("277387103")

new_json_data = dict()
new_json_data['id'] = json_data['id']
new_json_data['type'] = json_data['type']
new_json_data['title'] = json_data['title']
new_json_data['type'] = json_data['type']
new_json_data['version'] = {"number": json_data['version']['number'] + 1}
if not 'key' in json_data:
    new_json_data['key'] = json_data['space']['key']
    new_json_data['key'] = json_data['key']

new_json_data['body'] = {'storage':{'value':'<p>This is the updated text for the new page</p>','representation':'storage'}}

There should be something like this in new_json_data:

{'body': {'storage': {'representation': 'storage', 'value': '<p>This is the updated text for the new page</p>'}}, 'title': u'Test page', 'version': {'number': 2}, 'key': u'INFO', 'type': u'page', 'id': u'277387103'}

The response after sending JSON will be:

{"id":"277387103","type":"page","status":"current","title":"Test page", ....

And the content of the page should change.


What if we try to add a table?

new_json_data['body'] = {'storage':{'value':'<p><table><tr><th>head1</th><th>head2</th></tr><tr><td>value1</td><td>value2</td></tr><tr><td>value3</td><td>value4</td></tr></table></p>','representation':'storage'}}

Confluence table example

It’s simple!

Images and other attacments

Unfortunately, it’s not possible to send a picture in base64 this way =(

What if I attach a file to a page and use a link to it in code? It’s possible.

I’m uploading the file using this function:

def attach_file_to_page(page_id, filepath, filename):

    headers = {
        'X-Atlassian-Token': 'no-check',

    files = {
        'file': (filename, open(filepath, 'rb')),

    #delete attach with the same name
    response = requests.get("" + page_id + "/child/attachment", auth=(user, password))
    for attachment in json.loads(response.text)['results']:
        if attachment['title'] == filename:
            requests.delete("" + attachment['id'], headers=headers, auth=(user, password))

    #attack files
    response ="" +page_id+ "/child/attachment", headers=headers, files=files,
                     auth=(user, password))

Note that before you upload a file, you need to verify that the file with such name is not attached already or you will get an error. So I check all the attached files and delete files with the specified name.

URL of the file will be and we can use it in code:

functions_confluence.attach_file_to_page("277387103", "data/test.jpg", "test.jpg")
img_url = ""

new_json_data['body'] = {'storage':{'value': '<img src="'+img_url+'"></img>','representation':'storage'}}

{"id":"277387103","type":"page","status":"current","title":"Test page", ...

And test.jpg</ code> will appear on the page:

Confluence image attached

Useful curl one-liners you can also find on the official website here:

2 thoughts on “Confluence REST API for reading and updating wiki pages

  1. Lorenzo

    Hey Alex, just wanted to thank you for such a wonderfully fantastic tutorial – this is JUST what I’ve been scouring the internet for. Your code worked so well, and it is so incredibly well-written, I feel bad for essentially copying it but there is nothing I can do to improve it!

    I did, however, have a small question. By using the body/storage/value functions, writing new json data to the confluence page overrides the existing data and replaces it with the content of new_json_data. Is there a way to add new data while keeping existing data on the page, without having to include all the existing data in the new_json_data dictionary?

    Thank you again for your tutorial!

  2. Pingback: Asset Inventory for Network Perimeter: from Declarations to Active Scanning | Alexander V. Leonov

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.