Create IPUMS CPS Data Extracts

Below we provide examples in R, Python and curl showing how to work with the IPUMS API to create and manage CPS data extracts.

Get your key from your IPUMS user account management page at https://account.ipums.org/api_keys.

Load Libraries and Set Key

The Python examples use the ipumspy client library. You will need to install the ipumspy module into your Python environment if you haven’t already. You may also want to reference the ipumspy documentation site.

The R examples use the ipumsr R package. For instructions on installing the ipumsr, as well an overview of ipumsr API functionality, see the API vignette. You may also want to reference the ipumsr documentation site.

from ipumspy import IpumsApiClient, CpsExtract

# create new API client, set api key as env variable
ipums = IpumsApiClient(your_api_key)
library(ipumsr)
set_ipums_api_key("YOUR_API_KEY_HERE")
# set the IPUMS_API_KEY environment variable using bash shell
export IPUMS_API_KEY=YOUR_API_KEY_HERE

Submit a Data Extract Request

To submit a data extract request, you will either use the Python or R client library to construct an extract object, or alternately construct a JSON payload manually if you are using a tool like curl. Once you have your request formed, you will then submit it to the API.

The names to use for samples and variables in the data extract request can be discovered on our website.

# submit an extract request to the Microdata Extract API
extract = CpsExtract(
    ["cps2018_03s", "cps2019_03s"],
    ["AGE", "SEX", "RACE", "STATEFIP"],
    description = "My IPUMS CPS API-submitted extract"
)
ipums.submit_extract(extract)
# create a new extract object
extract_definition <- define_extract_cps(
    "This is an example extract to submit via API.",
    c("cps2018_03s","cps2019_03s"),
    c("AGE","SEX","RACE","STATEFIP")
)

# submit the extract to IPUMS CPS for processing
submitted_extract <- submit_extract(extract_definition)

# access the extract number, stored in the return value of submit_extract
submitted_extract$number
# construct the JSON payload manually and submit it 
curl --location --request POST 'https://api.ipums.org/extracts?collection=cps&version=beta' \
--header 'Authorization: YOUR_API_KEY_HERE' \
--header 'Content-Type: application/json' \
--data-raw '{
    "description": "Example extract",
    "data_structure": { 
        "rectangular": {
            "on": "P"
        }
    },
    "data_format": "fixed_width",
    "samples": {
      "cps2018_03s": {},
      "cps2019_03s": {}
    },
    "variables":{
      "AGE": {},
      "SEX": {},
      "RACE": {},
      "STATEFIP": {}
    }
}'

# A successful request will return a response that includes an extract number in the number attribute:

{
  "data_structure": {
    "rectangular": {
      "on": "P"
    }
  },
  "data_format": "fixed_width",
  "description": "my extract",
  "samples": {
    "cps2018_03s": {},
    "cps2019_03s": {}
  },
  "variables": {
    "YEAR": {
      "preselected": true
    },
    "SERIAL": {
      "preselected": true
    },
    "MONTH": {
      "preselected": true
    },
    "CPSID": {
      "preselected": true
    },
    "ASECFLAG": {
      "preselected": true
    },
    "ASECWTH": {
      "preselected": true
    },
    "STATEFIP": {},
    "PERNUM": {
      "preselected": true
    },
    "CPSIDP": {
      "preselected": true
    },
    "ASECWT": {
      "preselected": true
    },
    "AGE": {},
    "SEX": {},
    "RACE": {}
  },
  "download_links": {},
  "number": 363,
  "status": "queued"
}

Checking a Request’s Status

After submitting your extract request, you can use the Python and R client library functions to monitor the request’s status, or use the API directly using the extract’s number.

# check status of a request
extract_status = ipums.extract_status(extract)
# This will update the submitted_extract object with the latest extract status
submitted_extract <- get_extract_info(submitted_extract)

# Or, if you didn't capture the return value of submit_extract, but you know the extract number is 10, you can use:
submitted_extract <- get_extract_info("cps:10")

# Then you can check its status like this:
# status will be one of: `queued`, `started`, `produced` `canceled`, `failed` or `completed`
status <- submitted_extract$status

# Or if you just want to know if the extract is ready to download (TRUE or FALSE):
is_extract_ready(submitted_extract)
curl --request GET 'https://api.ipums.org/extracts/25?collection=cps&version=beta' --header 'Content-Type: application/json' --header 'Authorization: YOUR_API_KEY_HERE'

# A successful request will provide a response object like below. The exact fields may vary depending on how far along the extract is in processing.
# You will get a status such as `queued`, `started`, `produced` `canceled`, `failed` or `completed` in the status field. 

{
 "data_structure": {
 "rectangular": {
 "on": "P"
 }
 },
 "data_format": "fixed_width",
 "description": "my extract",
 "samples": {
 "cps2018_03s": {},
 "cps2019_03s": {}
 },
 "variables": {
 "YEAR": {
 "preselected": true
 },
 "SERIAL": {
 "preselected": true
 },
 "MONTH": {
 "preselected": true
 },
 "CPSID": {
 "preselected": true
 },
 "ASECFLAG": {
 "preselected": true
 },
 "ASECWTH": {
 "preselected": true
 },
 "STATEFIP": {},
 "PERNUM": {
 "preselected": true
 },
 "CPSIDP": {
 "preselected": true
 },
 "ASECWT": {
 "preselected": true
 },
 "AGE": {},
 "SEX": {},
 "RACE": {}
 },
 "download_links": {
 "sas_command_file": {
 "url": "https://api.ipums.org/downloads/cps/api/v1/extracts/1805434/cps_00363.sas",
 "bytes": 5906,
 "sha256": "8fa2432822a5722a18e01db4df217852a3853d691a447170cf1c0cead1176b83"
 },
 "data": {
 "url": "https://api.ipums.org/downloads/cps/api/v1/extracts/1805434/cps_00363.dat.gz",
 "bytes": 4162086,
 "sha256": "d16eafd692af2bb0e965f922da0e7733cab342364d50d8bef879565e8bee7f8d"
 },
 "stata_command_file": {
 "url": "https://api.ipums.org/downloads/cps/api/v1/extracts/1805434/cps_00363.do",
 "bytes": 11630,
 "sha256": "3cc666fb1fc9783fb5b79f448135c0be291b86adbe56f5e7fd62ab4c5ac43cad"
 },
 "R_command_file": {
 "url": "https://api.ipums.org/downloads/cps/api/v1/extracts/1805434/cps_00363.R",
 "bytes": 406,
 "sha256": "06e8d4ac8d8a6eeb043476f63e3b304a3cbcf04226768e28b59b41bf7db6f872"
 },
 "ddi_codebook": {
 "url": "https://api.ipums.org/downloads/cps/api/v1/extracts/1805434/cps_00363.xml",
 "bytes": 42154,
 "sha256": "b9c40bf3b67df38ddfd3822bfe30d4477a33a3b99aadaa42dfa4674a5667ce02"
 },
 "basic_codebook": {
 "url": "https://api.ipums.org/downloads/cps/api/v1/extracts/1805434/cps_00363.cbk",
 "bytes": 8274,
 "sha256": "f8112d7975900524fbde16b183aaff5a6a4a74db4c26102be0d37103d5e4092e"
 },
 "spss_command_file": {
 "url": "https://api.ipums.org/downloads/cps/api/v1/extracts/1805434/cps_00363.sps",
 "bytes": 5877,
 "sha256": "a2bd638c241ffc88908611415970a49dfed396ecd4ee3b3fa4ca77f0b71212ca"
 }
 },
 "number": 363,
 "status": "completed"
}

The client libraries also provide some convenience functions so that you don’t have to manually poll your extract’s status.

# convenience method that will wait until extract processing is complete before returning.
ipums.wait_for_extract(extract)
# This function call will wait until extract processing is complete before returning.
# By default, the function will periodically print messages while it waits for the extract to finish processing. Set argument verbose to FALSE if you want it to wait silently.
# The return value will include URLs for your extract files, so it is useful to capture that value by naming it.
downloadable_extract <- wait_for_extract(submitted_extract)
# This feature is not available without a client library.

Retrieving Your Extract

To retrieve a completed extract, we will once again use client library helper functions, or we can do so directly with the API using the extract’s number.

# download an extract to the current working directory
# you can use the optional download_dir="DIRECTORY_NAME" parameter
# to specify a different location
ipums.download_extract(extract)
# This will save the extract files to the current directory
# use the download_dir argument to specify a different location
# The return value is the path to the DDI codebook file, which can then be passed to read_ipums_micro to read the data
path_to_ddi_file <- download_extract(downloadable_extract)
data <- read_ipums_micro(path_to_ddi_file)
# download the data file using link that came back in extract request status object once completed
curl -H "Authorization: YOUR_API_KEY_HERE" https://api.ipums.org/downloads/cps/api/v1/extracts/1234567/cps_00363.dat.gz > my_ipums_cps_extract_363_dat.gz
# repeat for the other files e.g. codebook etc...

Now you are ready for further processing and analysis as you desire.

Get a Listing of Recent Extract Requests

You may also find it useful to get a historical listing of your extract requests.

# get a list of previous extracts as a list of extract definition dicts
# by default it will return the 10 most recent extracts. This can be overridden
# with the limit=## parameter. 
recent_extracts = ipums.retrieve_previous_extracts('cps')

# display extract IDs and descriptions
for ex in recent_extracts:
    print(f"{ex['number']}: {ex['description']}")

# produces output like:
# 34: My recent IPUMS extract
# 33: Added 2018 sample
# 32: Added more demographic variables
# 31: Revision of (Another extract)
# 30: Another extract
# 29: Revision of (Added SEX and RACE variables)
# 28: Added SEX and RACE variables
# 27: Added AGE variable
# 26: Revision of (My first extract)
# 25: My first extract
    
# re-download an extract to the current working directory
# this assume the data files haven't been purged from IPUMS servers yet
# which is done regularly to conserve disk space
ipums.download_extract(extract="17", collection="cps")

# Note: ipumspy does not have support for "reloading" a former extract into an extract object for revision, nor does it yet have support for resubmission.
# by default returns the 10 most recent. Use the how_many argument to override.
recent_extracts <- get_recent_extracts_info_list("cps", how_many = 5)

# recent_extracts is a list of extract objects:
# [[1]]
# Submitted IPUMS CPS extract number 50
# Description: My CPS extract
# Samples: (2 total) cps2018_03s, cps2019_03s
# Variables: (13 total) YEAR, SERIAL, MONTH, CPSID, ASECFLAG, ASECWTH, STATEFI...
# [[2]]
# Submitted IPUMS CPS extract number 49
# Description: Compare age-sex-race breakdowns 1976
# Samples: (2 total) cps1976_01s, cps1976_02b
# Variables: (12 total) YEAR, SERIAL, MONTH, CPSID, MISH, PERNUM, WTFINL, CPSI...
# [[3]]
# Submitted IPUMS CPS extract number 48
# Description: Compare age-sex-race breakdowns 1976
# Samples: (2 total) cps1976_01s, cps1976_02b
# Variables: (12 total) YEAR, SERIAL, MONTH, CPSID, MISH, PERNUM, WTFINL, CPSI...
# [[4]]
# Submitted IPUMS CPS extract number 47
# Description: Compare age-sex-race breakdowns 1976
# Samples: (2 total) cps1976_01s, cps1976_02b
# Variables: (12 total) YEAR, SERIAL, MONTH, CPSID, MISH, PERNUM, WTFINL, CPSI...
# [[5]]
# Submitted IPUMS CPS extract number 46
# Description: Compare age-sex-race breakdowns 1976
# Samples: (2 total) cps1976_01s, cps1976_02b
# Variables: (12 total) YEAR, SERIAL, MONTH, CPSID, MISH, PERNUM, WTFINL, CPSI...

# If you'd rather have a tibble / data.frame of extract definitions, use:
recent_extracts_tbl <- get_recent_extracts_info_tbl("cps", how_many = 5)
curl -X GET \
  https://api.ipums.org/extracts?collection=cps&version=beta \
  -H 'Content-Type: application/json' \
  -H 'Authorization: YOUR_API_KEY_HERE'

# If you omit an extract number in your API call, by default this will return the 10 most recent extract requests. To adjust the amount returned, you may optionally specify a `?limit=##` parameter to get the ## most recent extracts instead.

Revising and Resubmitting a Prior Extract (R/ipumsr-specific functionality)

The R client also has specific support for modifying and resubmitting prior extracts.

# This feature is not applicable without the ipumsr library.
# To pull down the definition of CPS extract number 17
old_extract <- get_extract_info("cps:17")
# Note that there are no spaces before or after the colon, and that the extract number doesn't need to be zero-padded.

# old_extract is now:
print(old_extract)
# Submitted IPUMS CPS extract number 17
# Samples: cps2018_03s, cps2019_03s
# Variables: YEAR, SAMPLE, SERIAL, CBSERIAL, HHWT...

# full list of variables:
old_extract$variables
# [1] "YEAR"     "SAMPLE"   "SERIAL"   "CBSERIAL" "HHWT"     "CLUSTER"  "STATEFIP" "STRATA"   "GQ"       "PERNUM"   "PERWT"    "SEX"      "AGE"      "RACE"

revised_extract <- add_to_extract(
    old_extract,
    samples = "cps2017_03s",
    vars = "EDUC"
)

revised_extract <- remove_from_extract(
    revised_extract,
    vars = "RACE"
)

# revised_extract has been reset to an unsubmitted state:
print(revised_extract)
# Unsubmitted IPUMS CPS extract
# Samples: cps2018_03s, cps2019_03s, cps2017_03s
# Variables: YEAR, SAMPLE, SERIAL, CBSERIAL, HHWT...

# revised extract has the updated samples and variables
revised_extract$samples
# [1] "cps2018_03s" "cps2019_03s" "cps2017_03s"
revised_extract$variables
# [1] "YEAR"     "SAMPLE"   "SERIAL"   "CBSERIAL" "HHWT"     "CLUSTER"  "STATEFIP" "STRATA"   "GQ"       "PERNUM"   "PERWT"    "SEX"      "AGE"      "EDUC"

newly_submitted_extract <- submit_extract(revised_extract)
# This feature is not applicable without the ipumsr library.