USA Spending API in C#

by Cyrus Hartwin Gomes

The USA Spending Website: https://www.usaspending.gov/

The USA Spending API: https://api.usaspending.gov/

These recipe examples were tested on December 12, 2023

Setup#

First, install the CURL package by typing the following command in the terminal:

!sudo apt install curl

Then, install the jq package by typing the following command in the terminal:

!sudo apt install jq

Now we create a directory for our projects to be created:

!mkdir USA_Spending

Finally, we will change the directory to the one we created:

%cd USA_Spending

1. Get agency names and toptier codes#

To obtain data from the API, it is useful to first build a dictionary containing agency names and toptier codes, the latter of which will be used to access subagency data.

First, we can initialize a folder for the current project that we are working on. And then change to that directory:

!mkdir APIdata
%cd APIdata

We utilize the %%file command to create the following makefile which will compile our program and create an executable.

%%file makefile

# Set the variable CC to gcc, which is used to build the program
CC=gcc

# Enable debugging information and enable all compiler warnings
CFLAGS=-g -Wall

# Set the bin variable as the name of the binary file we are creating
BIN=api_data

# Create the binary file with the name we put
all: $(BIN)

# Map any file ending in .c to a binary executable. 
# "$<" represents the .c file and "$@" represents the target binary executable
%: %.c

	# Compile the .c file using the gcc compiler with the CFLAGS and links 
	# resulting binary with the CURL library
	$(CC) $(CFLAGS) $< -o $@ -lcurl

# Clean target which removes specific files
clean:

	# Remove the binary file and an ".dSYM" (debug symbols for debugging) directories
	# the RM command used -r to remove directories and -f to force delete
	$(RM) -rf $(BIN) *.dSYM

The command is used again to create our .c file which contains the code for the program

%%file api_data.c

#include <curl/curl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* CURL program that retrieves api results from usaspendig with the given id. 
Custom property fields can be added */

int main (int argc, char* argv[]) {
    
    // If arguments are invalid just return
    if (argc < 2) {                                                                                      
        printf("Error. Please try again correctly. (./api_data -id [id])\n");
        return -1;
    }

    // Initialize the CURL HTTP connection
    CURL *curl = curl_easy_init();

    // Bits of the url that are joined together later                                                                      
    char api[] = "https://api.usaspending.gov";                            
    char url[1000];
    char default_id[] = "/api/v2/references/toptier_agencies/";

    // Check if CURL initialization is a success or not
    if (!curl) {                                                                                         
        fprintf(stderr, "init failed\n");
        return EXIT_FAILURE;
    }

    // When the command ./census_data -id is used
    if ((argc==2) && (strcmp(argv[1],"-id")==0)) {
        // Combine the api and the default id
        sprintf(url, "%s%s", api, default_id); 
    }
    
    // ./census_data -id [id]
    else if ((argc==3) && (strcmp(argv[1],"-id")==0)) {
        // Combine the api and the custom id
        sprintf(url, "%s%s", api, argv[2]);
    }

    // If the arguments are invalid just return
    else {        
        printf("./api_data -id [id]\n");                                                                                      
        curl_easy_cleanup(curl);
        return 0;
    }                                            

    // Set the url to which the HTTP request will be sent to
    // first parameter is for the initialized curl HTTP request, second for the option to be set, and third for the value to be set
    curl_easy_setopt(curl, CURLOPT_URL, url);

    // If result is not retrieved then output error
    CURLcode result = curl_easy_perform(curl);

    // If result is not retrieved then output error
    if (result != CURLE_OK) {                                                                            
        fprintf(stderr, "download problem: %s\n", curl_easy_strerror(result));
    }

    // Deallocate memory for the CURL connection
    curl_easy_cleanup(curl);                                                                            
    return EXIT_SUCCESS;
}
Writing api_data.c

The command is used again to create our .c file which contains the code for the program:

!make
!./api_data -id | jq '.["results"][0]'
{
  "agency_id": 1146,
  "toptier_code": "310",
  "abbreviation": "USAB",
  "agency_name": "Access Board",
  "congressional_justification_url": "https://www.access-board.gov/cj",
  "active_fy": "2023",
  "active_fq": "4",
  "outlay_amount": 9232761.09,
  "obligated_amount": 8863660.56,
  "budget_authority_amount": 11366458.51,
  "current_total_budget_authority_amount": 11889863708699.81,
  "percentage_of_total_budget_authority": 9.559788731373906e-07,
  "agency_slug": "access-board"
}
# Display total number of agencies in the data
!./api_data -id | jq '.["results"] | length'
108

Now we can create a dictionary containing the agency names as keys and the toptier codes as the data.

%%bash

# Get the raw data from the api
raw_data=$(./api_data -id | jq '.["results"]')

# Modify retrieved json data to retrieve agency name and top tier code
agencies_and_top_codes=$(echo "$raw_data" | jq -r '.[0:] | map({(.["agency_name"]): .["toptier_code"]}) | add')

# Print the modified data
echo "${agencies_and_top_codes}" | tee output.txt | head -n 5
{
  "Access Board": "310",
  "Administrative Conference of the U.S.": "302",
  "Advisory Council on Historic Preservation": "306",
  "African Development Foundation": "166",

We can also print the toptier code for a particular agency. This will be useful when building URLs to view other data from the API.

# Can be changed to any department
!cat output.txt | jq -r '.["Department of Transportation"]'
069

2. Retrieving Data from Subagencies#

The toptier_codes dictionary we created above contains every agency name in the API. For this example, we’ll look at the total obligations of each subagency of the Department of Defense.

%%bash

# Retrieve the toptier_code
# Can be set to the desired department
toptier_code=$(cat output.txt | jq -r '.["Department of Defense"]')

# Create complete url for retrieving API data
url="/api/v2/agency/${toptier_code}/sub_agency/"

# Get the raw data from the API
raw_data=$(./api_data -id "${url}"| jq '.["results"]')

# Modify retrieved JSON data to retrieve agency name and top tier code
subagencies=$(echo "$raw_data" | jq -r '.[0:] | map({(.["name"]): .["total_obligations"]}) | add')

# Sort the subagencies in alphabetical order
subagencies=$(echo "$subagencies" | jq 'to_entries | sort_by(.key) | from_entries')

# Print the modified data
echo "${subagencies}" | tee subagencies.json
{
  "Defense Advanced Research Projects Agency": 36296392,
  "Defense Health Agency": 15718671.85,
  "Defense Information Systems Agency": 1598100.49,
  "Defense Threat Reduction Agency": 1958308.27,
  "Department of the Air Force": 178875720.31,
  "Department of the Army": 503264075.56,
  "Department of the Navy": 250867292.31,
  "Missile Defense Agency": 42996.26,
  "Uniformed Services University of the Health Sciences": 33887146.44,
  "Washington Headquarters Services": 58951661
}

3. Accessing Fiscal Data Per Year#

We can use the API to examine the annual budget of an agency from 2017 onward.

%%bash

# Retrieve the toptier_code
# Can be set to the desired department
toptier_code=$(cat output.txt | jq -r '.["Department of Health and Human Services"]')

# Create complete url for retrieving api data
url="/api/v2/agency/${toptier_code}/budgetary_resources/"

# Get the raw data from the api
raw_data=$(./api_data -id "${url}"| jq '.["agency_data_by_year"]')

# Modify retrieved json data to retrieve agency name and top tier code
# All the years before 2023 are used
# Start from index 2 as the year 2024 and 2023 are skipped
# since fiscal years cannot be used as key, it is converted to strings
budgetary_resources_data=$(echo "$raw_data" | jq -r '.[2:] | map({(.["fiscal_year"] | tostring): .["agency_total_obligated"]}) | add')

# Sort the subagencies in alphabetical order
budgetary_resources_data=$(echo "$budgetary_resources_data" | jq 'to_entries | sort_by(.key) | from_entries')

# Print the modified data
echo "${budgetary_resources_data}" | tee budgetary_resources_data.txt
{
  "2017": 1646989531123.68,
  "2018": 1679128003253.74,
  "2019": 1814270463757.37,
  "2020": 2198882208891.79,
  "2021": 2355524286884.46,
  "2022": 2452969781323.39
}

4. Breaking Down Budget Categories#

We can use the API to view the breakdown the spending of a particular agency.

%%bash

# Retrieve the toptier_code
# Can be set to the desired department
toptier_code=$(cat output.txt | jq -r '.["Department of the Interior"]')

# Create complete url for retrieving api data
url="/api/v2/agency/${toptier_code}/obligations_by_award_category/"

# Get the raw data from the api
obligations_by_category_data=$(./api_data -id "${url}"| jq '.')

# Output the data to .txt file
echo "${obligations_by_category_data}" > obligations_by_category_data.txt

# Print the modified data
echo "${obligations_by_category_data}" | jq '.["results"]'
[
  {
    "category": "contracts",
    "aggregated_amount": 593973658.84
  },
  {
    "category": "direct_payments",
    "aggregated_amount": 505458682.16
  },
  {
    "category": "grants",
    "aggregated_amount": 309093580.28
  },
  {
    "category": "idvs",
    "aggregated_amount": 1033114.79
  },
  {
    "category": "loans",
    "aggregated_amount": 0
  },
  {
    "category": "other",
    "aggregated_amount": 416415.66
  }
]

It may be useful to limit our results to those with aggregated amounts greater than 0:

%%bash

# Reformat the data where category : aggregated_amount and aggregated amount > 0
formatted_data=$(cat obligations_by_category_data.txt | jq -r '.results | map(select(.aggregated_amount > 0) | {(.category): .aggregated_amount}) | add')

echo "${formatted_data}"
{
  "contracts": 593973658.84,
  "direct_payments": 505458682.16,
  "grants": 309093580.28,
  "idvs": 1033114.79,
  "other": 416415.66
}