10  How to Use the API

Note

On April 29th, we implemented a bugfix to address an error in the calculation of demographic disparity scores for supplemental demographic data (see Chapter 11 for more information on using supplemental data and Chapter 18 for more information on the bugfix).

The API has three endpoints we outline in the table below. We envision users calling the “Upload User Files” endpoint to begin a request, calling the “Get Output Data Status” endpoint to assess the request’s status, and calling the “Get Output Data” endpoint when their job finishes to receive the analysis results.

API Endpoints Index

Endpoint URL Method Description
Upload User Files https://equity-tool-api.urban.org/api/v1/upload-user-file/ POST Submit an analysis request to the Spatial Equity Data Tool
Get Output Data Status https://equity-tool-api.urban.org/api/v1/get-output-data-status/{file_id}/ GET Check the status of your submitted analysis request
Get Output Data https://equity-tool-api.urban.org/api/v1/get-output-data/{file_id}/ GET Obtain the results of a completed analysis request

The sedtR R Package

The sedtR R package wraps the three endpoints and allows interaction through user-friendly functions using the R programming language. While the workhorse functions in this package are operational and pass an extensive suite of tests, this package should be considered inbetaversion for the time being. Please create an issue or emailsedt@urban.orgif you have questions or see issues in the code. You can install the development version of sedtR from GitHub with:

# install.packages("devtools")
devtools::install_github("UrbanInstitute/sedtR")

The call_sedt_api() Function

The library(sedtr) function call_sedt_api() wraps the calls to the three separate API endpoints in a single function. The user will submit the request parameters as arguments to the call_sedt_api() function, which will submit the request to the “Upload User Files” endpoint, repeatedly ping the “Get Data Status” endpoint using sedtr::get_status()until the API successfully completes the analysis or returns an error. If the analysis successfully completes, the function then calls the “Get Output Data” endpoint using sedtr::get_output_data() and returns the results, formatted as shown below. Otherwise, the function returns an informative error message.

Example Function Call

library(sedtR)

call_sedt_api(
  resource_file_path = here("data", "sample", "dc_test_api_resource.csv"),
  resource_lat_column = "Y",
  resource_lon_column = "X",
  geo = "city",
  acs_data_year = "2019",
  demographic_file_path = here("data", "sample", "dc_test_api_demographic.csv"),
  demographic_geo_id_column = "GEOID",
  demographic_columns =  list(
    nh_white_pop = "nh_white_pop_margin",
    hispanic = "hispanic_margin",
    hispanic_men = "hispanic_men_margin"
  ),
  geographic_file_path = here("data", "sample", "dc_test_api_geographic.csv"),
  geographic_geo_id_column = "GEOID",
  geographic_columns = list(
    hispanic_men = "hispanic_men_margin",
    male_under_18 = "male_under_18_margin",
    female_under_18 = "female_under_18_margin",
    children = "children_margin"
  ),
  resource_weight = "ObjectId"
  )

Upload User Files Endpoint

Endpoint URL

https://equity-tool-api.urban.org/api/v1/upload-user-file/

Request Parameters

Field Name Value Type Definition Requirement
resource_lat_column string Name of column containing the latitudes for the observations in the resource datatset required
resource_lon_column string Name of column containing the longitudes for the observations in the resource dataset required
resource_file file Resource dataset file object required
demographic_file file Supplemental demographic dataset file object optional
demographic_geo_id_column string Name of the column containing the census tract FIPS codes/GEOID codes in the demographic dataset optional (conditionally required if demographic_file included)
demographic_columns dictionary Dictionary where the keys represent the names of the columns containing the additional demographic variables to be analyzed by the spatial equity data tools, and the values represent the names of the corresponding margin of error columns. If a given demographic column does not have a corresponding margin of error column, the value should be provided as NULL or NA. optional (conditionally required if demographic_file included)
geographic_file file Supplemental geographic dataset file object optional
geographic_geo_id_column string Name of the column containing the census tract FIPS codes/GEOID codes in the baseline dataset optional (conditionally required if geographic_file included)
geographic_columns dictionary Dictionary where the keys represent the names of the columns containing the additional baseline variables to be analyzed by the spatial equity data tools, and the values represent the names of the corresponding margin of error columns. If a given baseline column does not have a corresponding margin of error column, the value should be provided as NULL or NA. optional (conditionally required if geographic_file included)
resource_weight string Name of the column in the resource dataset that contains the weights that will be used in the analysis. optional
geo string Defines the geographic level of analysis. Either “city”, “county”, “state”, or “national”. required
acs_data_year string Defines the 5-year ACS data that will be used to for the built-in baseline and demographic data. Possible values are 2019 for the 2015-2019 ACS or 2021 for the 2017-2021 ACS. required

Response Format

The response is a JSON object documenting the input parameters selected by the user. Notably, there is a file_id key that stores the unique file identifier as a string.

Example Request and Response

library(sedtR)

call_upload_user_files(
  resource_file_path = here("data", "sample", "dc_test_api_resource.csv"),
  resource_lat_column = "Y",
  resource_lon_column = "X",
  geo = "city",
  acs_data_year = "2019",
  demographic_file_path = here("data", "sample", "dc_test_api_demographic.csv"),
  demographic_geo_id_column = "GEOID",
  demographic_columns =  list(
    nh_white_pop = "nh_white_pop_margin",
    hispanic = "hispanic_margin",
    hispanic_men = "hispanic_men_margin"
  ),
  geographic_file_path = here("data", "sample", "dc_test_api_geographic.csv"),
  geographic_geo_id_column = "GEOID",
  geographic_columns = list(
    hispanic_men = "hispanic_men_margin",
    male_under_18 = "male_under_18_margin",
    female_under_18 = "female_under_18_margin",
    children = "children_margin"
  ),
  resource_weight = "ObjectId"
  )
$status_code
[1] 201

$file_id
[1] "dec7f671-688e-41f7-b071-11cafede011f"
import requests
import json
import os

url = "https://equity-tool-api.urban.org/api/v1/upload-user-file/"

payload = {
    "resource_lat_column" : "Y",
    "resource_lon_column" : "X",
    "geo" : "city",
    "acs_data_year" : "2019",
    "demographic_geo_id_column" : "GEOID",
    "demographic_columns" : json.dumps({
      "nh_white_pop":"nh_white_pop_margin",
      "hispanic":"hispanic_margin",
      "hispanic_men":"hispanic_men_margin"
    }),
    "geographic_geo_id_column" : "GEOID",
    "geographic_columns" : json.dumps({
      "hispanic_men" : "hispanic_men_margin",
      "male_under_18" : "male_under_18_margin",
      "female_under_18" : "female_under_18_margin",
      "children" : "children_margin"
    }),
    "resource_weight" : "ObjectId"
    }

resource_file = open("../data/sample/dc_test_api_resource.csv", 
                      mode = 'r', 
                      newline='', 
                      encoding='utf-8-sig')
demographic_file = open("../data/sample/dc_test_api_demographic.csv", 
                         mode = 'r', 
                         newline='', 
                         encoding='utf-8-sig')
geographic_file = open("../data/sample/dc_test_api_geographic.csv",
                      mode = 'r', 
                      newline='', 
                      encoding='utf-8-sig')

r = requests.post(url, 
                  data = payload, 
                  files={"resource_file": resource_file, 
                         "demographic_file": demographic_file, 
                         "geographic_file": geographic_file}
                  )
{
 "id":566,
 "file_id":"71a22764-b236-4582-925c-ac91a1b526a4",
 "resource_file":"http://equity-tool-api.urban.org/media/documents/user-files/resource/65b6d42e-5031-4adc-a0cf-e37b9af80870_resource.csv",
 "resource_file_name":"71a22764-b236-4582-925c-ac91a1b526a4_resource.csv",
 "resource_lat_column":"Y",
 "resource_lon_column":"X",
 "demographic_file":"http://equity-tool-api.urban.org/media/documents/user-files/demographic/4565540d-0dfe-4fed-ba85-587fd9e5d747_demographic.csv",
 "demographic_file_name":"71a22764-b236-4582-925c-ac91a1b526a4_demographic.csv",
 "demographic_geo_id_column":"GEOID",
 "demographic_columns":{"nh_white_pop":"nh_white_pop_margin",
                        "hispanic":"hispanic_margin",
                        "hispanic_men":"hispanic_men_margin"
                        },
 "geographic_file":"http://equity-tool-api.urban.org/media/documents/user-files/geographic/db8e170a-b574-49fc-ac43-9ea6d46b453f_geographic.csv",
 "geographic_file_name":"71a22764-b236-4582-925c-ac91a1b526a4_geographic.csv",
 "geographic_geo_id_column":"GEOID",
 "geographic_columns":{"hispanic_men":"hispanic_men_margin",
                     "male_under_18":"male_under_18_margin",
                     "female_under_18":"female_under_18_margin",
                     "children":"children_margin"
                     },
 "resource_weight":"ObjectId",
 "geo":"city",
 "acs_data_year":"2019"
}

Get Data Status Endpoint

Endpoint URL

https://equity-tool-api.urban.org/api/v1/get-output-data-status/{file_id}/

Request Parameters

file_id is the only request parameter for this endpoint. The file_id is returned in the response of the “Upload User Files” endpoint and is a unique identifier for the API submission.

Response Format

The response content is a JSON object with keys next, previous, and null. The results key is associated with another JSON object with keys formdata, fileid, and file_exists. fileid is the same inputted file_id. file_exists returns either True or False. The formdata JSON object has three keys: updates, warnings, and error-messages. The “updates” JSON is populated as the code runs in the cloud. It has many keys, but most notably, there is a finished key that returns True when the equity calculations have been completed. warnings intuitively provides a list of keys that either returns booleans to indicate whether a certain warning should be noted or a list of variables that meet certain warning conditions. Similarly, errors has a list of keys that returns either booleans if that specific error is thrown or a list of variables from the supplemental dataset that leads to errors. For more information on the errors and warnings, see Chapter 12.

Example Request and Response

library(sedtR)
get_status("dec7f671-688e-41f7-b071-11cafede011f")
$`next`
NULL

$previous
NULL

$results
$results$formdata
$results$formdata$updates
$results$formdata$updates$started_processing
[1] TRUE

$results$formdata$updates$read_in_file
[1] TRUE

$results$formdata$updates$num_rows_file
[1] 66

$results$formdata$updates$num_filter_rows_dropped
[1] 0

$results$formdata$updates$num_null_rows_dropped
[1] 0

$results$formdata$updates$num_rows_dropped_total
[1] 9

$results$formdata$updates$num_rows_for_processing
[1] 58

$results$formdata$updates$num_rows_processed
[1] 58

$results$formdata$updates$num_rows_final
[1] 57

$results$formdata$updates$num_sub_geo_total
NULL

$results$formdata$updates$num_sub_geo_data
NULL

$results$formdata$updates$g_disp
[1] "Washington, DC"

$results$formdata$updates$sjoin_started
[1] TRUE

$results$formdata$updates$finished
[1] TRUE

$results$formdata$updates$`error-messages`
[1] FALSE

$results$formdata$updates$tool_geo
[1] "city"

$results$formdata$updates$tool_sub_geo
[1] "tract"

$results$formdata$updates$g_disp_fips
[1] "1150000"

$results$formdata$updates$total_time
[1] "4.02"


$results$formdata$warnings
$results$formdata$warnings$multiple_geographies_flag
[1] FALSE

$results$formdata$warnings$num_null_latlon_rows_dropped
[1] 0

$results$formdata$warnings$num_null_filter_rows_dropped
[1] 0

$results$formdata$warnings$num_null_weight_rows_dropped
[1] 0

$results$formdata$warnings$num_out_of_geography_rows_dropped
[1] 1

$results$formdata$warnings$multiple_geographies_list
NULL

$results$formdata$warnings$few_sub_geos_flag
[1] FALSE

$results$formdata$warnings$geographic_cols_any_missing_values
list()

$results$formdata$warnings$geographic_dropped_over_half_values_greater_than_total_pop
list()

$results$formdata$warnings$geographic_values_greater_than_total_pop
list()

$results$formdata$warnings$geographic_dropped_over_half_values_negative
list()

$results$formdata$warnings$geographic_dropped_any_values_negative_margin
list()

$results$formdata$warnings$geographic_values_negative
list()

$results$formdata$warnings$geographic_float_values
[1] "children_geographic_margin"

$results$formdata$warnings$demographic_cols_any_missing_values
list()

$results$formdata$warnings$demographic_dropped_over_half_values_greater_than_total_pop
list()

$results$formdata$warnings$demographic_values_greater_than_total_pop
[1] "nh_white_pop_demographic_margin"

$results$formdata$warnings$demographic_dropped_over_half_values_negative
list()

$results$formdata$warnings$demographic_dropped_any_values_negative_margin
list()

$results$formdata$warnings$demographic_values_negative
list()

$results$formdata$warnings$demographic_float_values
list()


$results$formdata$`error-messages`
$results$formdata$`error-messages`$`form-data-parameter-validation-failed`
[1] FALSE

$results$formdata$`error-messages`$data_readin_error
[1] FALSE

$results$formdata$`error-messages`$df_conversion_to_gdf_failed
[1] FALSE

$results$formdata$`error-messages`$filter_coltypes_mismatch
[1] FALSE

$results$formdata$`error-messages`$weight_coltypes_mismatch
[1] FALSE

$results$formdata$`error-messages`$filter_column_not_in_data
[1] FALSE

$results$formdata$`error-messages`$all_rows_filtered
[1] FALSE

$results$formdata$`error-messages`$pts_not_in_any_geography
[1] FALSE

$results$formdata$`error-messages`$sjoin_failed
[1] FALSE

$results$formdata$`error-messages`$unable_to_generate_presigned_urls
[1] FALSE



$results$fileid
[1] "dec7f671-688e-41f7-b071-11cafede011f"

$results$file_exists
[1] TRUE
url = "https://equity-tool-api.urban.org/api/v1/get-output-data-status/"
example_file_id = "71a22764-b236-4582-925c-ac91a1b526a4"
full_url = url + example_file_id + "/"
response = requests.get(full_url)
content = json.loads(response.content)

#To view the output in a more clean way:
content_dump = json.dumps(content, indent = 4)
print(content_dump)
{
    "next": null,
    "previous": null,
    "results": {
        "formdata": {
            "updates": {
                "started_processing": true,
                "read_in_file": true,
                "num_rows_file": 66,
                "num_filter_rows_dropped": 0,
                "num_null_rows_dropped": 0,
                "num_rows_dropped_total": 2,
                "num_rows_for_processing": 66,
                "num_rows_processed": 66,
                "num_rows_final": 64,
                "num_sub_geo_total": null,
                "num_sub_geo_data": null,
                "g_disp": "Washington, DC",
                "sjoin_started": true,
                "finished": true,
                "error-messages": false,
                "tool_geo": "city",
                "tool_sub_geo": "tract",
                "g_disp_fips": "1150000",
                "total_time": "4.94"
            },
            "warnings": {
                "multiple_geographies_flag": false,
                "num_null_latlon_rows_dropped": 0,
                "num_null_filter_rows_dropped": null,
                "num_null_weight_rows_dropped": 0,
                "num_out_of_geography_rows_dropped": 2,
                "multiple_geographies_list": null,
                "few_sub_geos_flag": false,
                "geographic_cols_any_missing_values": [],
                "geographic_dropped_over_half_values_greater_than_total_pop": [],
                "geographic_values_greater_than_total_pop": [],
                "geographic_dropped_over_half_values_negative": [],
                "geographic_dropped_any_values_negative_margin": [],
                "geographic_values_negative": [],
                "geographic_float_values": [
                    "children_geographic_margin"
                ],
                "demographic_cols_any_missing_values": [],
                "demographic_dropped_over_half_values_greater_than_total_pop": [],
                "demographic_values_greater_than_total_pop": [
                    "nh_white_pop_demographic_margin"
                ],
                "demographic_dropped_over_half_values_negative": [],
                "demographic_dropped_any_values_negative_margin": [],
                "demographic_values_negative": [],
                "demographic_float_values": []
            },
            "error-messages": {
                "form-data-parameter-validation-failed": false,
                "data_readin_error": false,
                "df_conversion_to_gdf_failed": false,
                "filter_coltypes_mismatch": false,
                "weight_coltypes_mismatch": false,
                "filter_column_not_in_data": false,
                "all_rows_filtered": false,
                "pts_not_in_any_geography": false,
                "sjoin_failed": false,
                "unable_to_generate_presigned_urls": false
            }
        },
        "fileid": "71a22764-b236-4582-925c-ac91a1b526a4",
        "file_exists": true
    }
}

Get Output Data Endpoint

Endpoint URL

https://equity-tool-api.urban.org/api/v1/get-output-data/{file_id}/

Request Parameters

file_id is the only request parameter for this endpoint. The file_id is returned in the responses of the upload-user-file/ and get-output-data-status/ endpoints and is a unique identifier for the API submission to upload-user-file/.

Response Format

The response returns a JSON with keys next, previous, and results. results has a JSON as a value. That JSON has keys result, fileid, which returns the file identifier, and file_exists, which returns a boolean indicating whether the file exists. result returns a JSON with keys geo_bias_data, which contains the geographic disparity scores, demographic_bias_data, which contains the demographic disparity scores, messages, which is itself a JSON with information on the process, download_links which containts a JSON of three keys (geo_bias_geojson, geo_bias_csv, and demographic_bias_csv which map to presigned URLS), and bbox and demographic_bias_sub_geo_list, which are only relevant for the GUI front end.

Note that this object allows two alternatives to access the geographic and demographic disparity scores: through geo_bias_data and demo_bias_data objects and through the presigned URLS in download_links. Given that the geographic bias data is inherently geographic, the API allows downloading as a CSV or a geojson.

Example Request and Response

library(sedtR)
output <- get_output_data("dec7f671-688e-41f7-b071-11cafede011f")
output$status_code
output$file_exists
output$file_id
head(output$geo_bias_data)
head(output$demo_bias_data)
#output$full_api_results #we don't display this because it is a long list (converted from a json)
200
TRUE
"dec7f671-688e-41f7-b071-11cafede011f"
[1] NA
[1] NA
url = "https://equity-tool-api.urban.org/api/v1/get-output-data/"
example_file_id = "71a22764-b236-4582-925c-ac91a1b526a4"
full_url = url + example_file_id + "/"
response = requests.get(full_url)

#Get a more clean output:
import pandas as pd
import geopandas as gpd
pd.set_option('display.max_columns', None)

#Get content from response and access geographic and demographic disparity scores
content = json.loads(response.content)
geo = content["results"]["result"]["geo_bias_data"]["features"]
dem = content["results"]["result"]["demographic_bias_data"]

#Print content without bulky geo_bias_data and demographic_bias_data
content["results"]["result"]["geo_bias_data"]= "Location of geographic bias data"
content["results"]["result"]["demographic_bias_data"]= "Location of demographic bias data"
content_for_print = json.dumps(content, indent = 4)
print(content_for_print)

#View Demographic data:
dem_df = pd.DataFrame(dem)
print(dem_df)

#View Geographic data:
geo_df = gpd.GeoDataFrame.from_features(geo)
geo_df.head()
{
    "next": null,
    "previous": null,
    "results": {
        "result": {
            "geo_bias_data": "Location of geographic bias data",
            "demographic_bias_data": "Location of demographic bias data",
            "messages": {
                "updates": {
                    "started_processing": true,
                    "read_in_file": true,
                    "num_rows_file": 66,
                    "num_filter_rows_dropped": 0,
                    "num_null_rows_dropped": 0,
                    "num_rows_dropped_total": 2,
                    "num_rows_for_processing": 66,
                    "num_rows_processed": 66,
                    "num_rows_final": 64,
                    "num_sub_geo_total": null,
                    "num_sub_geo_data": null,
                    "g_disp": "Washington, DC",
                    "sjoin_started": true,
                    "finished": false,
                    "error-messages": false,
                    "tool_geo": "city",
                    "tool_sub_geo": "tract",
                    "g_disp_fips": "1150000",
                    "total_time": null
                },
                "warnings": {
                    "multiple_geographies_flag": false,
                    "num_null_latlon_rows_dropped": 0,
                    "num_null_filter_rows_dropped": null,
                    "num_null_weight_rows_dropped": 0,
                    "num_out_of_geography_rows_dropped": 2,
                    "multiple_geographies_list": null,
                    "few_sub_geos_flag": false,
                    "geographic_cols_any_missing_values": [],
                    "geographic_dropped_over_half_values_greater_than_total_pop": [],
                    "geographic_values_greater_than_total_pop": [],
                    "geographic_dropped_over_half_values_negative": [],
                    "geographic_dropped_any_values_negative_margin": [],
                    "geographic_values_negative": [],
                    "geographic_float_values": [
                        "children_geographic_margin"
                    ],
                    "demographic_cols_any_missing_values": [],
                    "demographic_dropped_over_half_values_greater_than_total_pop": [],
                    "demographic_values_greater_than_total_pop": [
                        "nh_white_pop_demographic_margin"
                    ],
                    "demographic_dropped_over_half_values_negative": [],
                    "demographic_dropped_any_values_negative_margin": [],
                    "demographic_values_negative": [],
                    "demographic_float_values": []
                },
                "error-messages": {
                    "form-data-parameter-validation-failed": false,
                    "data_readin_error": false,
                    "df_conversion_to_gdf_failed": false,
                    "filter_coltypes_mismatch": false,
                    "weight_coltypes_mismatch": false,
                    "filter_column_not_in_data": false,
                    "all_rows_filtered": false,
                    "pts_not_in_any_geography": false,
                    "sjoin_failed": false,
                    "unable_to_generate_presigned_urls": false
                }
            },
            "download_links" : {
              "geo_bias_geojson" : "https://ui-sedt-prod.s3.amazonaws.com/output-data/geo-bias/geojson/...",
              "geo_bias_csv" : "https://ui-sedt-prod.s3.amazonaws.com/output-data/geo-bias/csv/",
              "demographic_bias_csv" : "'https://ui-sedt-prod.s3.amazonaws.com/output-data/demographic-bias/csv/..."
            }, 
            "bbox": [
                -77.119759,
                38.791645,
                -76.909395,
                38.99511
            ],
            "demographic_bias_sub_geo_list": []
        },
        "fileid": "71a22764-b236-4582-925c-ac91a1b526a4",
        "file_exists": true
    }
}

Rate Limiting

We only allow 300 API calls per hour (i.e., 1 every 12 seconds).