# 1: Performing API Operation

Ansible is not only limited to doing certain configuration , rather you can repurpose it to perform various API operation. Even you can create a playbook which can do an integration test by pipelining various api methods (GET, POST, DELETE, PUT) and validate the `json` data.

In this blog we will understand this by taking a simple case study.

**Problem statement :** Find out the top 10 `Julia` repository which has highest `GitHub` star

to do this we would need few things:

* GitHub api endpoint uri: [https://api.github.com/search/repositories](https://api.github.com/search/repositories)
    
* query filter : "q": “language:julia”, "sort": "stars", "per\_page": 100
    
* Authorization token header.
    
* store the response json.
    

Now let’s understand how can we achieve it using ansible. Ansible has a module called `ansible.builtin.uri` which can be used to perform this task. official

Lets Write the playbook

```yaml
- hosts: localhost
  gather_facts: false
  vars:
    Q: "language:julia"
    type: "repositories"
    per_page: "10"
  tasks:
    - name: Set GitHub PAT fact
      set_fact:
        github_pat: "{{ lookup('env', 'GITHUB_TOKEN') | default('', true) }}"
    - name: Set GitHub Address
      set_fact:
        github_addr: "https://api.github.com/search/repositories?q={{ Q }}&per_page={{ per_page }}&page=1&sort=stars"
    - name: get Response from and APi
      ansible.builtin.uri:
        url: "{{ github_addr }}"
        status_code: [200, 201]
        method: get
        headers:
          Authorization: "{{ github_pat }}"
        return_content: yes
      register: api_response
   - name: find length of the Repo
      ansible.builtin.debug:
        msg: "{{ item.name }}"
      
   - name: Add only names to a dictionary
     ansible.builtin.set_fact:
         result_repo_name: "{{ result_repo_name | default({}) | combine( item ) }}"
     loop: "{{ api_response.json.items }}"
```

in the above code `status_code` specifies what is the intended status code for this task. in this context 200, 201 . If the status code is beyond 200 or 201 i,e 3xx, 4xx, 5xx the task will fail.

if you closely observe we are using the `register` key. so the output response will be store inside the variable `api_response` and we can further extend this playbook to do other tasks.  
Like we did adding only names to a Dictionary.

So Now the important thing is what all are the debugging steps:

* when you use `ansible.builtin.uri` the response which get registered in the variable are under a Dictionary called `json`
    

if you debug `api_response` you can find the the output as below :

```json

  "date": "Wed, 26 Mar 2025 14:32:21 GMT",
  "elapsed": 0,
  "failed": false,
  "json": {
      "incomplete_results": false,
      "items": [
          {
              "allow_forking": true,
              "archived": false,
              "assignees_url": "https://api.github.com/repos/JuliaLang/julia/assignees{/user}",
              "blobs_url": "https://api.github.com/repos/JuliaLang/julia/git/blobs{/sha}",
              "branches_url": "https://api.github.com/repos/JuliaLang/julia/branches{/branch}",
              "clone_url": "https://github.com/JuliaLang/julia.git",
              "fork": false,
              "forks": 5556,
              "forks_count": 5556,
              "forks_url": "https://api.github.com/repos/JuliaLang/julia/forks",
              "full_name": "JuliaLang/julia",
              "git_commits_url": "https://api.github.com/repos/JuliaLang/julia/git/commits{/sha}",
              "git_refs_url": "h
              },
              {
                "allow_forking": true,
                "archived": false,
                "assignees_url": "https://api.github.com/repos/fonsp/Pluto.jl/assignees{/user}",
                "blobs_url": "https://api.github.com/repos/fonsp/Pluto.jl/git/blobs{/sha}",
                "branches_url": "https://api.github.com/repos/fonsp/Pluto.jl/branches{/branch}",
                "clone_url": "https://github.com/fonsp/Pluto.jl.git",
                "fork": false,
                "forks": 5556,
                "forks": 294,
                "forks_count": 294,
                "forks_url": "https://api.github.com/repos/fonsp/Pluto.jl/forks",
                "full_name": "fonsp/Pluto.jl",
                "git_commits_url": "https://api.github.com/repos/fonsp/Pluto.jl/git/commits{/sha}"

              }
      ]
```

Now if you want to display let’s say `blobs_url` of a repo you need to write it as below :  
  
❌ Wrong Way:

```yaml
   - name: Add only names to a dictionary
     ansible.builtin.set_fact:
         result_repo_name: "{{ item.blob_url }}"
     loop: "{{ api_response[json][items] }}"
```

✅ Right approaches:

```yaml
   - name: Add only names to a dictionary
     ansible.builtin.set_fact:
         result_repo_name: "{{ item.blob_url }}"
     loop: "{{ api_response.json.items }}"
```

### Why ?

  
Coz if you use `[]` notation it expect whatever inside `[]` should be a **static** variable and ansible want you to define that variable may be you can get `variable` undefined error. But when you use `.` notation it is to access the variable inside a dictionary which is **dynamic.**  
  
🖋️ **Tip of the Day (Jinja2) :**  
  
you can not use Jinja2 template in `vars`. Jinja2 template are only get populated during run times. `set_fact` in ansible are the way to declare a varriable in any programming language.

❌ Wrong approaches:

```yaml
  - hosts: localhost
    gather_facts: false
    vars:
      github_pat: "{{ lookup('env', 'GITHUB_TOKEN') | default('', true) }}"
    - name: debug github token
      debug:
        var: "{{ github_pat }}"
```

✅ Right approaches:

```yaml
- hosts: localhost
  gather_facts: false

  tasks:
    - name: Set GitHub PAT fact
      set_fact:
        github_pat: "{{ lookup('env', 'GITHUB_TOKEN') | default('', true) }}"
    - name: debug github token
      debug:
        var: "{{ github_pat }}"
```

More things to unlock 🔓. Happy Ansibling !
