Ansible Tower API: Automate Jobs, Inventories & Workflows via REST (Guide)
By Luca Berton · Published 2024-01-01 · Category: troubleshooting
Complete guide to Ansible Tower API (AWX/AAP). Launch jobs, manage inventories, create workflows, authenticate with tokens, query job status, and integrate.
The AWX/Ansible Tower/AAP REST API lets you automate everything the web UI can do — launch jobs, manage inventories, create workflows, and integrate Ansible into CI/CD pipelines programmatically.
API Basics
Base URL
# AWX / Ansible Tower
https://tower.example.com/api/v2/
# AAP 2.x (Controller)
https://controller.example.com/api/v2/
# API root — lists all available endpoints
curl -s https://tower.example.com/api/v2/ | python3 -m json.tool
Authentication
Personal Access Token (Recommended)
# Create a token
curl -s -X POST \
-H "Content-Type: application/json" \
-u admin:password \
https://tower.example.com/api/v2/tokens/ \
-d '{"description": "CI/CD Token", "scope": "write"}'
# Use token in requests
curl -s -H "Authorization: Bearer <token>" \
https://tower.example.com/api/v2/me/
Basic Auth
curl -s -u admin:password \
https://tower.example.com/api/v2/me/
OAuth2 Token
# Create application
curl -s -X POST \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
https://tower.example.com/api/v2/applications/ \
-d '{
"name": "CI Pipeline",
"organization": 1,
"authorization_grant_type": "password",
"client_type": "confidential"
}'
See also: AWX vs Ansible Tower vs AAP: Key Differences Explained (2026)
Launch Jobs
Launch a Job Template
# Launch by ID
curl -s -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
https://tower.example.com/api/v2/job_templates/42/launch/ \
-d '{
"extra_vars": {
"target_env": "production",
"app_version": "2.1.0"
},
"limit": "webservers",
"tags": "deploy"
}'
Launch and Wait for Completion
#!/bin/bash
TOKEN="your-token"
TOWER="https://tower.example.com"
# Launch job
RESPONSE=$(curl -s -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
"$TOWER/api/v2/job_templates/42/launch/" \
-d '{"extra_vars": {"version": "2.1.0"}}')
JOB_ID=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
echo "Job launched: $JOB_ID"
# Poll until complete
while true; do
STATUS=$(curl -s \
-H "Authorization: Bearer $TOKEN" \
"$TOWER/api/v2/jobs/$JOB_ID/" | \
python3 -c "import sys,json; print(json.load(sys.stdin)['status'])")
echo "Status: $STATUS"
case "$STATUS" in
successful) echo "Job succeeded!"; exit 0;;
failed|error|canceled) echo "Job failed!"; exit 1;;
*) sleep 10;;
esac
done
Launch with Ansible uri Module
- name: Launch Tower job template
ansible.builtin.uri:
url: "https://tower.example.com/api/v2/job_templates/42/launch/"
method: POST
headers:
Authorization: "Bearer {{ tower_token }}"
body_format: json
body:
extra_vars:
target_env: production
status_code: 201
register: job_launch
- name: Wait for job completion
ansible.builtin.uri:
url: "https://tower.example.com/api/v2/jobs/{{ job_launch.json.id }}/"
headers:
Authorization: "Bearer {{ tower_token }}"
register: job_status
until: job_status.json.status in ['successful', 'failed', 'error', 'canceled']
retries: 60
delay: 30
- name: Fail if job failed
ansible.builtin.fail:
msg: "Tower job {{ job_launch.json.id }} failed: {{ job_status.json.status }}"
when: job_status.json.status != 'successful'
Manage Inventories
List Inventories
curl -s -H "Authorization: Bearer $TOKEN" \
"$TOWER/api/v2/inventories/" | python3 -m json.tool
Create Inventory
curl -s -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
"$TOWER/api/v2/inventories/" \
-d '{
"name": "Production",
"organization": 1,
"description": "Production servers"
}'
Add Host to Inventory
curl -s -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
"$TOWER/api/v2/inventories/5/hosts/" \
-d '{
"name": "web-prod-01",
"variables": "ansible_host: 10.0.1.10\nhttp_port: 8080"
}'
Sync Inventory Source
curl -s -X POST \
-H "Authorization: Bearer $TOKEN" \
"$TOWER/api/v2/inventory_sources/3/update/"
See also: Ansible Automation: Complete Guide to IT Automation with Playbook Examples
Manage Job Templates
List Job Templates
curl -s -H "Authorization: Bearer $TOKEN" \
"$TOWER/api/v2/job_templates/" | \
python3 -c "
import sys, json
data = json.load(sys.stdin)
for jt in data['results']:
print(f\"{jt['id']:4d} | {jt['name']:40s} | {jt['playbook']}\")
"
Create Job Template
curl -s -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
"$TOWER/api/v2/job_templates/" \
-d '{
"name": "Deploy Web App",
"job_type": "run",
"inventory": 5,
"project": 3,
"playbook": "deploy.yml",
"credential": 2,
"ask_variables_on_launch": true,
"ask_limit_on_launch": true,
"extra_vars": "app_version: latest"
}'
Workflows
Launch Workflow
curl -s -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
"$TOWER/api/v2/workflow_job_templates/10/launch/" \
-d '{
"extra_vars": {
"release_version": "3.0.0",
"environment": "staging"
}
}'
See also: Ansible Development: Write Custom Modules, Plugins & Collections
CI/CD Integration
GitHub Actions
# .github/workflows/deploy.yml
name: Deploy via Tower
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Launch Ansible Tower job
run: |
RESPONSE=$(curl -s -X POST \
-H "Authorization: Bearer ${{ secrets.TOWER_TOKEN }}" \
-H "Content-Type: application/json" \
"${{ secrets.TOWER_URL }}/api/v2/job_templates/${{ vars.JOB_TEMPLATE_ID }}/launch/" \
-d '{"extra_vars": {"version": "${{ github.sha }}"}}')
JOB_ID=$(echo "$RESPONSE" | jq -r '.id')
echo "job_id=$JOB_ID" >> $GITHUB_OUTPUT
# Wait for completion
for i in $(seq 1 60); do
STATUS=$(curl -s \
-H "Authorization: Bearer ${{ secrets.TOWER_TOKEN }}" \
"${{ secrets.TOWER_URL }}/api/v2/jobs/$JOB_ID/" | jq -r '.status')
echo "Attempt $i: $STATUS"
[ "$STATUS" = "successful" ] && exit 0
[ "$STATUS" = "failed" ] || [ "$STATUS" = "error" ] && exit 1
sleep 15
done
echo "Timeout waiting for job" && exit 1
Python Client
import requests
import time
class TowerClient:
def __init__(self, url, token):
self.url = url.rstrip('/')
self.session = requests.Session()
self.session.headers['Authorization'] = f'Bearer {token}'
self.session.headers['Content-Type'] = 'application/json'
def launch_job(self, template_id, extra_vars=None, limit=None):
payload = {}
if extra_vars:
payload['extra_vars'] = extra_vars
if limit:
payload['limit'] = limit
resp = self.session.post(
f'{self.url}/api/v2/job_templates/{template_id}/launch/',
json=payload
)
resp.raise_for_status()
return resp.json()['id']
def wait_for_job(self, job_id, timeout=1800, interval=15):
end_time = time.time() + timeout
while time.time() < end_time:
resp = self.session.get(f'{self.url}/api/v2/jobs/{job_id}/')
status = resp.json()['status']
if status == 'successful':
return True
if status in ('failed', 'error', 'canceled'):
raise Exception(f'Job {job_id} {status}')
time.sleep(interval)
raise TimeoutError(f'Job {job_id} timed out')
# Usage
tower = TowerClient('https://tower.example.com', 'my-token')
job_id = tower.launch_job(42, extra_vars={'version': '2.1.0'})
tower.wait_for_job(job_id)
FAQ
How do I authenticate with the Tower/AWX API?
Use a Personal Access Token (recommended): create one via the API or web UI, then pass Authorization: Bearer in headers. Basic auth (-u user:pass) also works but tokens are more secure for automation.
How do I launch a job template via the API?
Send a POST to /api/v2/job_templates/. The response includes the job ID which you can poll at /api/v2/jobs/ until status is successful, failed, or canceled.
What is the difference between Tower API and AWX API?
They're the same API — AWX is the upstream open-source project and Ansible Tower/AAP Controller is the Red Hat commercial product. The API endpoints, authentication, and behavior are identical.
How do I pass extra variables when launching a job?
Include extra_vars in the POST body: {"extra_vars": {"key": "value"}}. The job template must have ask_variables_on_launch: true or accept survey/extra vars.
How do I integrate Tower with CI/CD pipelines?
Use the REST API to launch jobs from GitHub Actions, GitLab CI, or Jenkins. POST to the launch endpoint, capture the job ID, then poll the job status endpoint in a loop until completion.
Conclusion
• Authentication: Use Bearer tokens for automation, Basic auth for testing • Launch jobs:POST /api/v2/job_templates//launch/
• Check status: GET /api/v2/jobs// → poll until status is terminal
• Manage resources: Full CRUD on inventories, templates, credentials, projects
• CI/CD: Integrate with any pipeline that can make HTTP calls
Related Articles
• AWX vs Ansible Tower vs AAP Comparison • Ansible uri Module: HTTP/REST API Calls • Ansible CI/CD Pipeline IntegrationCategory: troubleshooting