Introduction

ansible.builtin.uri makes HTTP requests from your playbooks — call REST APIs, check health endpoints, download files, submit forms, and authenticate with tokens. It's Ansible's equivalent of curl.

Basic Requests

# GET request
- name: Check website
  ansible.builtin.uri:
    url: https://example.com
    return_content: true
  register: response

- debug:
    msg: "Status: {{ response.status }}, Body: {{ response.content[:100] }}"

# Health check
- name: Check app health
  ansible.builtin.uri:
    url: http://localhost:8080/health
    status_code: 200

Parameters

ParameterDefaultDescription
url(required)Request URL
methodGETHTTP method
body—Request body
body_formatrawjson, form-urlencoded, form-multipart, raw
headers—Request headers dict
status_code200Expected status code(s)
return_contentfalseInclude body in response
timeout30Request timeout (seconds)
validate_certstrueVerify SSL certificates
url_username—Basic auth username
url_password—Basic auth password
force_basic_authfalseSend auth header preemptively
dest—Download to file path
creates—Skip if file exists
follow_redirectssafeall, safe, none

POST Request (JSON)

# Create a resource
- name: Create user via API
  ansible.builtin.uri:
    url: https://api.example.com/users
    method: POST
    body_format: json
    body:
      name: "John Doe"
      email: "john@example.com"
      role: "admin"
    headers:
      Authorization: "Bearer {{ api_token }}"
    status_code: [200, 201]
  register: result

- debug:
    msg: "Created user ID: {{ result.json.id }}"

PUT Request

- name: Update configuration
  ansible.builtin.uri:
    url: "https://api.example.com/config/{{ config_id }}"
    method: PUT
    body_format: json
    body:
      setting: "new_value"
      enabled: true
    headers:
      Authorization: "Bearer {{ api_token }}"
    status_code: 200

DELETE Request

- name: Delete resource
  ansible.builtin.uri:
    url: "https://api.example.com/resources/{{ resource_id }}"
    method: DELETE
    headers:
      Authorization: "Bearer {{ api_token }}"
    status_code: [200, 204]

Authentication

# Bearer token
- name: API call with token
  ansible.builtin.uri:
    url: https://api.example.com/data
    headers:
      Authorization: "Bearer {{ vault_api_token }}"

# Basic auth
- name: API with basic auth
  ansible.builtin.uri:
    url: https://api.example.com/data
    url_username: "{{ api_user }}"
    url_password: "{{ vault_api_password }}"
    force_basic_auth: true

# API key in header
- name: API with key
  ansible.builtin.uri:
    url: https://api.example.com/data
    headers:
      X-API-Key: "{{ vault_api_key }}"

Download Files

- name: Download file
  ansible.builtin.uri:
    url: https://releases.example.com/app-v2.tar.gz
    dest: /tmp/app-v2.tar.gz
    creates: /tmp/app-v2.tar.gz    # Skip if exists

Practical Patterns

Health Check with Retries

- name: Wait for app to be healthy
  ansible.builtin.uri:
    url: http://localhost:8080/health
    status_code: 200
    return_content: true
  register: health
  until: health.status == 200 and 'healthy' in (health.content | default(''))
  retries: 30
  delay: 10

API Pagination

- name: Fetch all pages
  ansible.builtin.uri:
    url: "https://api.example.com/items?page={{ item }}&per_page=100"
    headers:
      Authorization: "Bearer {{ api_token }}"
    return_content: true
  register: pages
  loop: "{{ range(1, total_pages + 1) | list }}"

Webhook Notification

- name: Notify Slack
  ansible.builtin.uri:
    url: "{{ slack_webhook_url }}"
    method: POST
    body_format: json
    body:
      text: "Deployment of {{ app_version }} to {{ inventory_hostname }} completed ✅"
    status_code: 200

- name: Notify Teams
  ansible.builtin.uri:
    url: "{{ teams_webhook_url }}"
    method: POST
    body_format: json
    body:
      "@type": MessageCard
      summary: "Deploy Complete"
      text: "{{ app_version }} deployed to {{ ansible_play_hosts | length }} hosts"

Deploy with API Calls

---
- name: Blue-Green Deployment
  hosts: localhost
  tasks:
    - name: Remove server from load balancer
      ansible.builtin.uri:
        url: "https://lb.example.com/api/pools/web/members/{{ target_server }}"
        method: DELETE
        headers:
          Authorization: "Bearer {{ lb_token }}"
        status_code: [200, 204]

    - name: Deploy to server
      ansible.builtin.include_tasks: deploy.yml

    - name: Wait for health check
      ansible.builtin.uri:
        url: "http://{{ target_server }}:8080/health"
        status_code: 200
      retries: 10
      delay: 5

    - name: Add server back to load balancer
      ansible.builtin.uri:
        url: "https://lb.example.com/api/pools/web/members"
        method: POST
        body_format: json
        body:
          address: "{{ target_server }}"
          port: 8080
        headers:
          Authorization: "Bearer {{ lb_token }}"
        status_code: [200, 201]

CRUD Operations

vars:
  api_base: https://api.example.com
  api_headers:
    Authorization: "Bearer {{ vault_api_token }}"
    Content-Type: application/json

tasks:
  # Create
  - name: Create item
    ansible.builtin.uri:
      url: "{{ api_base }}/items"
      method: POST
      body_format: json
      body: { name: "widget", price: 9.99 }
      headers: "{{ api_headers }}"
      status_code: 201
    register: created

  # Read
  - name: Get item
    ansible.builtin.uri:
      url: "{{ api_base }}/items/{{ created.json.id }}"
      headers: "{{ api_headers }}"
      return_content: true

  # Update
  - name: Update item
    ansible.builtin.uri:
      url: "{{ api_base }}/items/{{ created.json.id }}"
      method: PUT
      body_format: json
      body: { price: 12.99 }
      headers: "{{ api_headers }}"

  # Delete
  - name: Delete item
    ansible.builtin.uri:
      url: "{{ api_base }}/items/{{ created.json.id }}"
      method: DELETE
      headers: "{{ api_headers }}"
      status_code: 204

Troubleshooting

SSL Certificate Errors

# Skip cert validation (dev/test only!)
- uri:
    url: https://self-signed.example.com
    validate_certs: false

Unexpected Status Code

# Accept multiple status codes
- uri:
    url: https://api.example.com/resource
    status_code: [200, 201, 204, 404]
  register: result

- debug:
    msg: "Resource {{ 'exists' if result.status != 404 else 'not found' }}"

Response Parsing

# JSON response is auto-parsed into .json
- uri:
    url: https://api.example.com/data
    return_content: true
  register: result

- debug:
    msg: "Name: {{ result.json.name }}, Count: {{ result.json.items | length }}"

Conclusion

ansible.builtin.uri is your HTTP client for automation. Use it for health checks, API integrations, webhook notifications, and deployment orchestration. Always store tokens and passwords in Vault. Use retries + delay for health checks that need time. Use status_code to accept expected responses. The body_format: json option handles serialization automatically. For anything you'd use curl for, use uri instead.