AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

AAP 2.6 REST API Guide: Automate the Automation Platform

By Luca Berton · Published 2024-01-01 · Category: troubleshooting

Master the AAP 2.6 REST API for programmatic management. Launch jobs, manage inventories, create credentials, query job results, and integrate AAP with CI/CD.

AAP 2.6 API Overview

Every action available in the AAP web UI is also available through the REST API. The API enables: • Programmatic job execution — trigger automation from scripts, CI/CD, or external tools • Integration with ITSM platforms — ServiceNow, Jira, PagerDuty • Self-service portals — build custom UIs on top of AAP • Bulk operations — manage hundreds of hosts, credentials, or templates • Monitoring and reporting — extract job data for dashboards

See also: AAP 2.6 CI/CD Pipeline Integration: GitOps Workflows with Jenkins, GitLab, and GitHub Actions

Authentication

Personal Access Tokens (PAT)

The recommended authentication method:

# Create a token via API
curl -s -k -X POST \
  -H "Content-Type: application/json" \
  -u "admin:password" \
  "https://gateway.example.org/api/controller/v2/tokens/" \
  -d '{"description": "CI/CD Pipeline Token", "scope": "write"}'

# Response { "id": 1, "token": "abc123def456...", "description": "CI/CD Pipeline Token", "scope": "write" }

Using Tokens

# Bearer token (recommended)
curl -s -k -H "Authorization: Bearer abc123def456..." \
  "https://gateway.example.org/api/controller/v2/ping/"

# Or via OAuth2 header curl -s -k -H "Authorization: Bearer abc123def456..." \ "https://gateway.example.org/api/controller/v2/me/"

OAuth2 Application Tokens

For service-to-service integration:

- name: Create OAuth2 application
  ansible.builtin.uri:
    url: "https://gateway.example.org/api/controller/v2/applications/"
    method: POST
    headers:
      Authorization: "Bearer {{ admin_token }}"
      Content-Type: "application/json"
    body_format: json
    body:
      name: "ServiceNow Integration"
      organization: 1
      authorization_grant_type: "password"
      client_type: "confidential"
    validate_certs: false

API Endpoints Reference

Core Resources

| Endpoint | Description | |----------|-------------| | /api/controller/v2/ping/ | Health check (no auth required) | | /api/controller/v2/me/ | Current user info | | /api/controller/v2/config/ | Platform configuration | | /api/controller/v2/dashboard/ | Summary statistics |

Automation Resources

| Endpoint | Description | |----------|-------------| | /api/controller/v2/job_templates/ | Job templates CRUD | | /api/controller/v2/workflow_job_templates/ | Workflow templates | | /api/controller/v2/jobs/ | Job history and status | | /api/controller/v2/workflow_jobs/ | Workflow job history | | /api/controller/v2/inventories/ | Inventories | | /api/controller/v2/hosts/ | Hosts | | /api/controller/v2/groups/ | Host groups | | /api/controller/v2/projects/ | SCM projects | | /api/controller/v2/credentials/ | Credentials (metadata only) | | /api/controller/v2/schedules/ | Job schedules | | /api/controller/v2/notification_templates/ | Notifications |

Platform Resources

| Endpoint | Description | |----------|-------------| | /api/controller/v2/organizations/ | Organizations | | /api/controller/v2/teams/ | Teams | | /api/controller/v2/users/ | Users | | /api/controller/v2/roles/ | RBAC roles | | /api/controller/v2/instances/ | Mesh instances | | /api/controller/v2/instance_groups/ | Instance groups | | /api/controller/v2/execution_environments/ | EE images | | /api/controller/v2/activity_stream/ | Audit log |

See also: Ansible Tower API: Automate Jobs, Inventories & Workflows via REST (Guide)

Common API Operations

Launch a Job Template

# Launch with defaults
curl -s -k -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  "https://gateway.example.org/api/controller/v2/job_templates/15/launch/"

# Launch with parameters curl -s -k -X POST \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "extra_vars": {"app_version": "2.4.1", "deploy_env": "staging"}, "limit": "webservers", "inventory": 5, "credentials": [3, 7] }' \ "https://gateway.example.org/api/controller/v2/job_templates/15/launch/"

# Response includes job ID { "id": 423, "url": "/api/controller/v2/jobs/423/", "status": "pending", ... }

Wait for Job Completion

# Poll job status
JOB_ID=423
while true; do
  STATUS=$(curl -s -k -H "Authorization: Bearer $TOKEN" \
    "https://gateway.example.org/api/controller/v2/jobs/$JOB_ID/" | \
    jq -r '.status')
  echo "Job $JOB_ID: $STATUS"
  case $STATUS in
    successful|failed|error|canceled) break ;;
  esac
  sleep 5
done

Get Job Output

# Job stdout
curl -s -k -H "Authorization: Bearer $TOKEN" \
  "https://gateway.example.org/api/controller/v2/jobs/423/stdout/?format=txt"

# Job events (structured) curl -s -k -H "Authorization: Bearer $TOKEN" \ "https://gateway.example.org/api/controller/v2/jobs/423/job_events/?page_size=50" | \ jq '.results[] | {counter: .counter, event: .event, task: .task, host: .host_name, changed: .changed}'

Create a Host

curl -s -k -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "web05.example.com",
    "inventory": 3,
    "enabled": true,
    "variables": "{\"ansible_host\": \"192.168.1.15\", \"http_port\": 8080}"
  }' \
  "https://gateway.example.org/api/controller/v2/hosts/"

Search and Filter

# Search job templates by name
curl -s -k -H "Authorization: Bearer $TOKEN" \
  "https://gateway.example.org/api/controller/v2/job_templates/?search=patching"

# Filter hosts by group curl -s -k -H "Authorization: Bearer $TOKEN" \ "https://gateway.example.org/api/controller/v2/hosts/?group__name=webservers"

# Filter jobs by status and date curl -s -k -H "Authorization: Bearer $TOKEN" \ "https://gateway.example.org/api/controller/v2/jobs/?status=failed&finished__gte=2026-04-01"

# Order by field curl -s -k -H "Authorization: Bearer $TOKEN" \ "https://gateway.example.org/api/controller/v2/jobs/?order_by=-finished&page_size=10"

Python API Client

Using requests

#!/usr/bin/env python3
"""AAP API client example"""
import requests
import time
import json

class AAPClient: def __init__(self, gateway_url, token, verify_ssl=False): self.base_url = f"{gateway_url}/api/controller/v2" self.session = requests.Session() self.session.headers.update({ "Authorization": f"Bearer {token}", "Content-Type": "application/json" }) self.session.verify = verify_ssl

def launch_job(self, template_id, extra_vars=None, limit=None): """Launch a job template and return job ID""" payload = {} if extra_vars: payload["extra_vars"] = extra_vars if limit: payload["limit"] = limit

resp = self.session.post( f"{self.base_url}/job_templates/{template_id}/launch/", json=payload ) resp.raise_for_status() return resp.json()

def wait_for_job(self, job_id, timeout=600, interval=5): """Wait for job completion, return final status""" elapsed = 0 while elapsed < timeout: resp = self.session.get(f"{self.base_url}/jobs/{job_id}/") resp.raise_for_status() job = resp.json() status = job["status"] if status in ("successful", "failed", "error", "canceled"): return job time.sleep(interval) elapsed += interval raise TimeoutError(f"Job {job_id} did not complete in {timeout}s")

def get_job_output(self, job_id): """Get job stdout""" resp = self.session.get( f"{self.base_url}/jobs/{job_id}/stdout/", params={"format": "txt"} ) return resp.text

def list_inventories(self): """List all inventories""" resp = self.session.get(f"{self.base_url}/inventories/") resp.raise_for_status() return resp.json()["results"]

def add_host(self, inventory_id, hostname, variables=None): """Add a host to an inventory""" payload = { "name": hostname, "inventory": inventory_id, "enabled": True } if variables: payload["variables"] = json.dumps(variables) resp = self.session.post(f"{self.base_url}/hosts/", json=payload) resp.raise_for_status() return resp.json()

# Usage client = AAPClient("https://gateway.example.org", "your-token")

# Launch and wait job = client.launch_job(15, extra_vars={"app_version": "2.4.1"}) print(f"Launched job {job['id']}")

result = client.wait_for_job(job["id"]) print(f"Job finished: {result['status']}")

if result["status"] == "failed": print(client.get_job_output(job["id"]))

Using awxkit (Official SDK)

from awxkit import api
from awxkit.config import config

config.base_url = "https://gateway.example.org" config.credentials = {"default": {"username": "admin", "password": "password"}}

root = api.Api() root.load_session().get()

# Launch job jt = root.get("/api/controller/v2/job_templates/15/").results job = jt.launch(extra_vars={"env": "staging"}) job.wait_until_completed() print(f"Status: {job.status}")

See also: Automate Ansible Collection Testing with GitHub Actions

CI/CD Integration Examples

GitHub Actions

# .github/workflows/deploy.yml
name: Deploy via AAP
on:
  push:
    branches: [main]

jobs: deploy: runs-on: ubuntu-latest steps: - name: Trigger AAP deployment run: | RESPONSE=$(curl -s -k -X POST \ -H "Authorization: Bearer ${{ secrets.AAP_TOKEN }}" \ -H "Content-Type: application/json" \ -d '{"extra_vars": {"app_version": "${{ github.sha }}"}}' \ "${{ secrets.AAP_URL }}/api/controller/v2/job_templates/${{ vars.DEPLOY_TEMPLATE_ID }}/launch/")

JOB_ID=$(echo $RESPONSE | jq -r '.id') echo "Launched AAP job $JOB_ID"

# Wait for completion while true; do STATUS=$(curl -s -k \ -H "Authorization: Bearer ${{ secrets.AAP_TOKEN }}" \ "${{ secrets.AAP_URL }}/api/controller/v2/jobs/$JOB_ID/" | \ jq -r '.status') echo "Status: $STATUS" case $STATUS in successful) exit 0 ;; failed|error|canceled) exit 1 ;; esac sleep 10 done

GitLab CI

# .gitlab-ci.yml
deploy:
  stage: deploy
  script:
    - |
      JOB_ID=$(curl -s -k -X POST \
        -H "Authorization: Bearer $AAP_TOKEN" \
        -H "Content-Type: application/json" \
        -d "{\"extra_vars\": {\"version\": \"$CI_COMMIT_SHA\"}}" \
        "$AAP_URL/api/controller/v2/job_templates/$DEPLOY_TEMPLATE_ID/launch/" | \
        jq -r '.id')
      echo "AAP Job: $JOB_ID"

Jenkins Pipeline

pipeline {
    agent any
    stages {
        stage('Deploy via AAP') {
            steps {
                script {
                    def response = httpRequest(
                        url: "${AAP_URL}/api/controller/v2/job_templates/${DEPLOY_TEMPLATE_ID}/launch/",
                        httpMode: 'POST',
                        customHeaders: [[name: 'Authorization', value: "Bearer ${AAP_TOKEN}"]],
                        contentType: 'APPLICATION_JSON',
                        requestBody: '{"extra_vars": {"version": "' + env.BUILD_TAG + '"}}',
                        ignoreSslErrors: true
                    )
                    def jobId = readJSON(text: response.content).id
                    echo "Launched AAP job ${jobId}"
                }
            }
        }
    }
}

ServiceNow Integration

Trigger AAP from ServiceNow Incident

# ServiceNow Business Rule or Flow Designer script
import requests

AAP_URL = "https://gateway.example.org" AAP_TOKEN = "your-service-token"

def trigger_remediation(incident): """Auto-remediate based on incident category""" template_map = { "disk_full": 15, # Disk Cleanup template "service_down": 22, # Service Restart template "cert_expiry": 31, # Certificate Renewal template }

template_id = template_map.get(incident.category) if not template_id: return

response = requests.post( f"{AAP_URL}/api/controller/v2/job_templates/{template_id}/launch/", headers={"Authorization": f"Bearer {AAP_TOKEN}"}, json={ "extra_vars": { "incident_number": incident.number, "target_host": incident.cmdb_ci, "priority": incident.priority } }, verify=False )

# Update incident with AAP job link job_id = response.json()["id"] incident.work_notes = f"Automated remediation triggered: AAP Job #{job_id}" incident.update()

Webhook Receivers

AAP can receive webhooks from GitHub, GitLab, and other systems:

- name: Configure GitHub webhook on job template
  ansible.platform.job_template:
    controller_host: "{{ gateway_url }}"
    controller_username: "{{ controller_user }}"
    controller_password: "{{ controller_pass }}"
    name: "Deploy on Push"
    webhook_service: "github"
    webhook_credential: "GitHub Webhook Secret"
    state: present

The webhook URL is:

POST https://gateway.example.org/api/controller/v2/job_templates/{id}/github/

Configure this URL in GitHub repository Settings → Webhooks.

Pagination and Bulk Operations

Handle Paginated Results

def get_all_results(client, endpoint):
    """Fetch all pages of a paginated API response"""
    results = []
    url = f"{client.base_url}{endpoint}"
    while url:
        resp = client.session.get(url)
        resp.raise_for_status()
        data = resp.json()
        results.extend(data["results"])
        url = data.get("next")
    return results

# Get all hosts across all pages all_hosts = get_all_results(client, "/hosts/?page_size=200") print(f"Total hosts: {len(all_hosts)}")

FAQ

What is the API rate limit?

AAP does not enforce a hard rate limit by default, but rapid API calls can impact Controller performance. For bulk operations, add small delays between requests. For monitoring, scrape the metrics endpoint instead of polling job status.

Can I use the API to export/import configurations?

Yes. Use the API to export job templates, inventories, credentials (metadata only), and workflows as JSON. The awx-manage export_assets and awx-manage import_assets CLI commands provide bulk export/import functionality.

Is the API backward compatible?

The /api/v2/ endpoint has been stable since Ansible Tower 3.x. AAP 2.6 adds new endpoints under /api/controller/v2/ but maintains backward compatibility. Check the API changelog in the release notes for deprecations.

How do I find the template ID for API calls?

Browse the API in your browser at https://gateway.example.org/api/controller/v2/ — it provides a browsable interface. Or search by name: /api/controller/v2/job_templates/?search=patching.

Can I use GraphQL instead of REST?

No. AAP provides a REST API only. There is no GraphQL endpoint.

Conclusion

The AAP 2.6 REST API transforms the platform from a UI-driven tool into a programmable automation engine. Whether integrating with CI/CD pipelines, ITSM platforms, or building custom self-service portals, the API provides complete programmatic access to every AAP capability.

Related Articles

AAP 2.6 Architecture and Components: Complete GuideAAP 2.6 RBAC and Gateway APIAAP 2.6 Configuration as Code with ansible.platformAAP 2.6 Workflow Templates: Advanced Multi-Step Automation GuideAAP 2.6 Job Templates and Inventories: Complete Configuration Guide

Category: troubleshooting

Browse all Ansible tutorials · AnsiblePilot Home