Confluence: API Automation with Python
Table of Contents
Section titled “Table of Contents”- Prerequisites and Setup
- Authentication
- Creating a Basic Page
- Updating Existing Pages
- Working with Confluence Storage Format
- Adding Rich Content
- Managing Page Hierarchy
- Best Practices
Prerequisites and Setup
Section titled “Prerequisites and Setup”Install the Atlassian Python API library:
pip install atlassian-python-apiOr using uv:
uv pip install atlassian-python-apiSet up environment variables for authentication:
export CONFLUENCE_URL="https://your-domain.atlassian.net"export CONFLUENCE_EMAIL="your-email@example.com"export CONFLUENCE_API_TOKEN="your-api-token"You can generate an API token from: https://id.atlassian.com/manage-profile/security/api-tokens
Authentication
Section titled “Authentication”import osfrom atlassian import Confluence
# Initialize Confluence clientconfluence = Confluence( url=os.environ["CONFLUENCE_URL"], username=os.environ["CONFLUENCE_EMAIL"], password=os.environ["CONFLUENCE_API_TOKEN"], cloud=True # Set to False for Confluence Server)
# Test connectiontry: user = confluence.get_current_user() print(f"Connected as: {user['displayName']}")except Exception as e: print(f"Authentication failed: {e}")Creating a Basic Page
Section titled “Creating a Basic Page”import osfrom atlassian import Confluence
confluence = Confluence( url=os.environ["CONFLUENCE_URL"], username=os.environ["CONFLUENCE_EMAIL"], password=os.environ["CONFLUENCE_API_TOKEN"], cloud=True)
def create_page(space_key, title, body, parent_id=None): """ Create a new Confluence page
Args: space_key: The space key (e.g., "TEAM", "DOCS") title: Page title body: Page content in Confluence storage format (HTML-like) parent_id: Optional parent page ID to nest under
Returns: Created page ID """ try: page = confluence.create_page( space=space_key, title=title, body=body, parent_id=parent_id, type='page', representation='storage' ) print(f"Created page: {page['_links']['webui']}") return page['id'] except Exception as e: print(f"Failed to create page: {e}") return None
# Example usagespace = "TEAM"title = "Getting Started with Python"body = """<h1>Introduction</h1><p>This page covers Python basics for our team.</p><h2>Key Concepts</h2><ul> <li>Variables and data types</li> <li>Functions and modules</li> <li>Error handling</li></ul>"""
page_id = create_page(space, title, body)Updating Existing Pages
Section titled “Updating Existing Pages”def update_page(page_id, new_body, new_title=None): """ Update an existing Confluence page
Args: page_id: The page ID to update new_body: New content in storage format new_title: Optional new title (keeps existing if None) """ try: # Get current page to retrieve version number page = confluence.get_page_by_id(page_id, expand='version')
current_version = page['version']['number'] current_title = page['title']
# Update the page updated_page = confluence.update_page( page_id=page_id, title=new_title or current_title, body=new_body, version_comment="Updated via Python script", minor_edit=False # Set to True for minor edits )
print(f"Updated page to version {current_version + 1}") return updated_page except Exception as e: print(f"Failed to update page: {e}") return None
# Example: Append content to existing pagepage_id = "123456789"existing_page = confluence.get_page_by_id(page_id, expand='body.storage')existing_body = existing_page['body']['storage']['value']
new_content = "<h2>New Section</h2><p>Additional information added.</p>"updated_body = existing_body + new_content
update_page(page_id, updated_body)Working with Confluence Storage Format
Section titled “Working with Confluence Storage Format”Confluence uses a storage format similar to HTML with some special macros. Here are common patterns:
# Headersh1 = "<h1>Main Title</h1>"h2 = "<h2>Subtitle</h2>"
# Paragraphs and formattingparagraph = "<p>This is a paragraph with <strong>bold</strong> and <em>italic</em> text.</p>"
# Listsunordered_list = """<ul> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li></ul>"""
ordered_list = """<ol> <li>First step</li> <li>Second step</li> <li>Third step</li></ol>"""
# Linksinternal_link = '<a href="/wiki/spaces/TEAM/pages/123456">Link to another page</a>'external_link = '<a href="https://example.com">External link</a>'
# Code blockscode_block = """<ac:structured-macro ac:name="code" ac:schema-version="1"> <ac:parameter ac:name="language">python</ac:parameter> <ac:plain-text-body><![CDATA[def hello_world(): print("Hello, World!") ]]></ac:plain-text-body></ac:structured-macro>"""
# Tablestable = """<table> <thead> <tr> <th>Name</th> <th>Role</th> <th>Department</th> </tr> </thead> <tbody> <tr> <td>Alice</td> <td>Engineer</td> <td>Development</td> </tr> <tr> <td>Bob</td> <td>Manager</td> <td>Operations</td> </tr> </tbody></table>"""
# Info panel (macro)info_panel = """<ac:structured-macro ac:name="info"> <ac:rich-text-body> <p>This is an informational message.</p> </ac:rich-text-body></ac:structured-macro>"""
# Warning panelwarning_panel = """<ac:structured-macro ac:name="warning"> <ac:rich-text-body> <p>This is a warning message.</p> </ac:rich-text-body></ac:structured-macro>"""Adding Rich Content
Section titled “Adding Rich Content”def create_documentation_page(space_key, title, sections): """ Create a well-formatted documentation page
Args: space_key: Confluence space key title: Page title sections: List of dicts with 'title', 'content', 'code' keys """ body_parts = [f"<h1>{title}</h1>"]
for section in sections: # Section title body_parts.append(f"<h2>{section['title']}</h2>")
# Section content body_parts.append(f"<p>{section['content']}</p>")
# Optional code block if 'code' in section: code_macro = f""" <ac:structured-macro ac:name="code" ac:schema-version="1"> <ac:parameter ac:name="language">{section.get('language', 'python')}</ac:parameter> <ac:plain-text-body><![CDATA[{section['code']}]]></ac:plain-text-body> </ac:structured-macro> """ body_parts.append(code_macro)
body = "\n".join(body_parts) return create_page(space_key, title, body)
# Example usagesections = [ { "title": "Installation", "content": "Install the required dependencies using pip:", "code": "pip install requests\npip install pandas", "language": "bash" }, { "title": "Usage", "content": "Here's a basic example:", "code": "import requests\n\nresponse = requests.get('https://api.example.com')\nprint(response.json())", "language": "python" }]
create_documentation_page("DOCS", "API Client Setup", sections)Managing Page Hierarchy
Section titled “Managing Page Hierarchy”def create_page_tree(space_key, page_structure, parent_id=None): """ Recursively create a hierarchy of pages
Args: space_key: Confluence space key page_structure: Dict with 'title', 'body', and optional 'children' keys parent_id: Parent page ID (None for root level)
Returns: Dict mapping page titles to their IDs """ created_pages = {}
# Create current page page_id = create_page( space_key=space_key, title=page_structure['title'], body=page_structure['body'], parent_id=parent_id )
if page_id: created_pages[page_structure['title']] = page_id
# Create child pages for child in page_structure.get('children', []): child_pages = create_page_tree(space_key, child, page_id) created_pages.update(child_pages)
return created_pages
# Example: Create documentation structuredocs_structure = { "title": "Python Development Guide", "body": "<h1>Python Development Guide</h1><p>Welcome to our Python documentation.</p>", "children": [ { "title": "Getting Started", "body": "<h1>Getting Started</h1><p>Setup instructions for new developers.</p>", "children": [ { "title": "Environment Setup", "body": "<h1>Environment Setup</h1><p>Install Python and configure your IDE.</p>" }, { "title": "Project Structure", "body": "<h1>Project Structure</h1><p>Understanding our codebase layout.</p>" } ] }, { "title": "Best Practices", "body": "<h1>Best Practices</h1><p>Coding standards and guidelines.</p>" } ]}
created = create_page_tree("DOCS", docs_structure)print(f"Created {len(created)} pages")Best Practices
Section titled “Best Practices”1. Error Handling and Validation
Section titled “1. Error Handling and Validation”def safe_create_page(space_key, title, body, parent_id=None, max_retries=3): """Create page with retry logic and validation""" import time
# Validate inputs if not space_key or not title or not body: raise ValueError("space_key, title, and body are required")
# Check if page already exists existing = confluence.get_page_by_title(space_key, title) if existing: print(f"Page '{title}' already exists with ID: {existing['id']}") return existing['id']
# Retry logic for attempt in range(max_retries): try: page = confluence.create_page( space=space_key, title=title, body=body, parent_id=parent_id ) return page['id'] except Exception as e: if attempt < max_retries - 1: wait_time = 2 ** attempt # Exponential backoff print(f"Attempt {attempt + 1} failed, retrying in {wait_time}s...") time.sleep(wait_time) else: raise Exception(f"Failed to create page after {max_retries} attempts: {e}")2. Batch Operations
Section titled “2. Batch Operations”def bulk_create_pages(space_key, pages, delay=0.5): """ Create multiple pages with rate limiting
Args: space_key: Confluence space key pages: List of dicts with 'title', 'body', 'parent_id' keys delay: Delay between requests in seconds """ import time
results = [] for i, page_data in enumerate(pages): print(f"Creating page {i+1}/{len(pages)}: {page_data['title']}")
try: page_id = create_page( space_key=space_key, title=page_data['title'], body=page_data['body'], parent_id=page_data.get('parent_id') ) results.append({'title': page_data['title'], 'id': page_id, 'status': 'success'}) except Exception as e: results.append({'title': page_data['title'], 'error': str(e), 'status': 'failed'})
# Rate limiting if i < len(pages) - 1: time.sleep(delay)
return results3. Template-Based Page Creation
Section titled “3. Template-Based Page Creation”from string import Template
def create_page_from_template(space_key, title, template_path, variables): """ Create a page using an HTML template
Args: space_key: Confluence space key title: Page title template_path: Path to HTML template file variables: Dict of variables to substitute in template """ with open(template_path, 'r') as f: template_content = f.read()
template = Template(template_content) body = template.safe_substitute(variables)
return create_page(space_key, title, body)
# Example template file (meeting_notes_template.html):# <h1>$meeting_title</h1># <p><strong>Date:</strong> $date</p># <p><strong>Attendees:</strong> $attendees</p># <h2>Agenda</h2># <p>$agenda</p># <h2>Action Items</h2># <p>$action_items</p>
# Usage:variables = { 'meeting_title': 'Sprint Planning Meeting', 'date': '2026-01-28', 'attendees': 'Alice, Bob, Carol', 'agenda': 'Review backlog and assign tasks', 'action_items': 'TBD during meeting'}
create_page_from_template( 'TEAM', 'Sprint Planning - Jan 28', 'meeting_notes_template.html', variables)4. Search and Update Pattern
Section titled “4. Search and Update Pattern”def find_and_update_pages(space_key, search_term, update_function): """ Find pages matching criteria and apply updates
Args: space_key: Confluence space key search_term: CQL search term update_function: Function that takes page_id and returns new body """ # Search for pages cql = f'space = "{space_key}" AND text ~ "{search_term}"' results = confluence.cql(cql)
updated_pages = [] for result in results.get('results', []): page_id = result['content']['id'] page_title = result['content']['title']
try: # Get current content page = confluence.get_page_by_id(page_id, expand='body.storage') current_body = page['body']['storage']['value']
# Apply update function new_body = update_function(current_body)
# Update page update_page(page_id, new_body) updated_pages.append(page_title) print(f"Updated: {page_title}") except Exception as e: print(f"Failed to update {page_title}: {e}")
return updated_pages
# Example: Add a banner to all pages mentioning "deprecated"def add_deprecation_banner(body): banner = """ <ac:structured-macro ac:name="warning"> <ac:rich-text-body> <p><strong>DEPRECATED:</strong> This content is outdated. See the new documentation.</p> </ac:rich-text-body> </ac:structured-macro> """ return banner + body
updated = find_and_update_pages("DOCS", "deprecated", add_deprecation_banner)print(f"Updated {len(updated)} pages")