├── .gitignore ├── EXAMPLE_facebook_config.yml ├── Instructions_to_obtain_FB_Ad_Manager_Credentials.pdf ├── LICENSE ├── README.md ├── Zagheni_et_al_IUSSP_2017_FB_ads.pdf ├── facebook_ads.R ├── facebook_ads.Rproj ├── facebook_ads.pdf ├── facebook_ads_intro.Rmd ├── facebook_ads_intro.pdf └── targeting_specs ├── targeting_spec_00.json ├── targeting_spec_01.json ├── targeting_spec_02.json ├── targeting_spec_03.json └── targeting_spec_us_states.json /.gitignore: -------------------------------------------------------------------------------- 1 | facebook_config.yml 2 | .Rproj.user 3 | -------------------------------------------------------------------------------- /EXAMPLE_facebook_config.yml: -------------------------------------------------------------------------------- 1 | access_token: "PASTE YOUR ACCESS TOKEN HERE" 2 | ad_account_id: "act_############" 3 | -------------------------------------------------------------------------------- /Instructions_to_obtain_FB_Ad_Manager_Credentials.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccgilroy/r-estimates-fb-ads/c0b7025a0046309f963768beab210bae849a7d84/Instructions_to_obtain_FB_Ad_Manager_Credentials.pdf -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Connor Gilroy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Demographic estimates from the Facebook Marketing API 2 | 3 | Connor Gilroy, University of Washington 4 | 5 | This module introduces you to obtaining estimates of populations of Facebook 6 | users stratified by demographic characteristics and geographies. It does this 7 | by accessing the Facebook Marketing API using `httr`. 8 | 9 | We use `httr` because there exist no dedicated R packages for the Facebook Ads 10 | API. Existing R packages (`SocialMediaLab`, `RFacebook`) are designed for 11 | retrieving public information from the Facebook Graph API, such as comments on 12 | Facebook Pages. 13 | 14 | The activities in this module will allow you to retrieve a novel kind of social 15 | media data that has previously only been accessed using other tools and 16 | languages, such as Python. The module thus illustrates the versatility and 17 | utility of R and `httr` for digital demography. 18 | 19 | Additionally, we present examples of contemporary demographic research using the 20 | Facebook Marketing API in slides found 21 | [here](https://github.com/ccgilroy/r-estimates-fb-ads/blob/master/Zagheni_et_al_IUSSP_2017_FB_ads.pdf) 22 | and 23 | [here](https://github.com/ccgilroy/r-estimates-fb-ads/blob/master/facebook_ads_intro.pdf). 24 | 25 | ## Using this module 26 | 27 | The easiest way to work with this module is as an "R Project." To do this, 28 | open the folder that you downloaded from Dropbox or Github and click on the 29 | `facebook_ads.Rproj` file (not the .R file!). This will open the R Project 30 | in RStudio, and make it convenient to access all of the files you need. 31 | 32 | See this page for more information on R Projects in RStudio: 33 | 34 | https://support.rstudio.com/hc/en-us/articles/200526207-Using-Projects 35 | 36 | For this module, you will need to edit two types of files that your computer 37 | may not have a program set up to open by default: .json (JSON) and .yml (YAML) 38 | files. Unless you are familiar with using text editors to write code, we recommend editing these files directly in RStudio. 39 | 40 | If you have already opened the R Project, you can easily access existing 41 | files in the project folder through the "Files" pane of RStudio (which shares 42 | a panel with Plots, Packages, and Help); otherwise, you can open them through 43 | RStudio > File > "Open file...". 44 | 45 | ## Obtaining Facebook Ads Manager Credentials 46 | 47 | The two pieces of information to send with every Facebook Ads API request are 48 | an Ads Account ID and a valid access token. 49 | 50 | The authentication process is similar to that for the Twitter API, in that you 51 | create an application and use it to generate an OAuth access token. It is 52 | moderately complex, so follow the instructions carefully. 53 | 54 | The overarching steps to accomplish are the following: 55 | 56 | - Create a verified Facebook account 57 | - Create a developer app 58 | - Get a Marketing API access token for this app 59 | - Create an Ads Account by starting to create an ad 60 | 61 | Detailed instructions, with screenshots, may be found in the included set of slides, 62 | [Obtaining Facebook Ad Manager Credentials](https://github.com/ccgilroy/r-estimates-fb-ads/blob/master/Instructions_to_obtain_FB_Ad_Manager_Credentials.pdf) 63 | 64 | Social media platforms frequently make small changes to their developer 65 | platforms, so do not be surprised if you notice some minor differences between 66 | the screenshots in the instructions and your own experience. 67 | 68 | Once you have successfully obtained a valid access token and your Ads Account ID, 69 | be sure to store these credentials in the following way: 70 | 71 | - Copy your access token and your account id into EXAMPLE_facebook_config.yml; use the format "act_#########" for your account id 72 | - **RENAME** the file to "facebook_config.yml" 73 | -------------------------------------------------------------------------------- /Zagheni_et_al_IUSSP_2017_FB_ads.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccgilroy/r-estimates-fb-ads/c0b7025a0046309f963768beab210bae849a7d84/Zagheni_et_al_IUSSP_2017_FB_ads.pdf -------------------------------------------------------------------------------- /facebook_ads.R: -------------------------------------------------------------------------------- 1 | #' --- 2 | #' title: "Demographic estimates from the Facebook Marketing API" 3 | #' author: "Connor Gilroy" 4 | #' date: "`r Sys.Date()`" 5 | #' output: 6 | #' beamer_presentation: 7 | #' theme: "metropolis" 8 | #' latex_engine: xelatex 9 | #' highlight: pygments 10 | #' df_print: kable 11 | #' header-includes: 12 | #' - \setmonofont[Mapping=]{Fira Mono} 13 | #' --- 14 | #' 15 | 16 | #' ## Packages 17 | #+ warning=FALSE, message=FALSE 18 | library(httr) 19 | library(jsonlite) 20 | library(stringr) 21 | library(tidyverse) 22 | library(yaml) 23 | 24 | #' ## Setup: config and url 25 | 26 | #' We need two pieces of information from the config file: 27 | #' an access token, and an ads account. 28 | #' Make sure the ads account number is prefixed with "act_". 29 | fb_cfg <- yaml.load_file("facebook_config.yml") 30 | 31 | #' The url for accessing 'reach' estimates is based off of the ads account, 32 | #' so we construct it by pasting strings onto the base url 33 | fb_url <- "https://graph.facebook.com/v2.10" 34 | fb_ads_url <- str_c(fb_url, fb_cfg$ad_account_id, 35 | "reachestimate", sep = "/") 36 | 37 | #' ## Example 1: US population on Facebook 38 | targeting_spec <- '{"geo_locations": {"countries": ["US"]}}' 39 | 40 | #' This is as simple as a targeting spec can be. 41 | #' We're specifying just a geographic location: 42 | #' one country, the United States. 43 | #' 44 | #' The braces (`{ }`) and brackets (`[ ]`) define a hierarchy. 45 | #' As requests get more complex, it will become clear why we need them. 46 | #' 47 | 48 | #' ## Make request 49 | #' 50 | #' In addition to the access token and targeting spec, 51 | #' there are two required parameters we need to include. 52 | #' Their values aren't important. 53 | #' 54 | 55 | fb_query <- list( 56 | access_token = fb_cfg$access_token, 57 | currency = "USD", 58 | optimize_for = "NONE", 59 | targeting_spec = targeting_spec 60 | ) 61 | 62 | r <- GET(fb_ads_url, query = fb_query) 63 | 64 | #' ## Look at response 65 | 66 | prettify(content(r, as = "text")) 67 | 68 | content(r, as = "parsed")$data$users 69 | 70 | #' ## Example 2: Young men and women in Washington State 71 | #' 72 | #' In the state of Washington, how many men and women 73 | #' between the ages of 20 and 30 are on Facebook? 74 | #' 75 | #' To answer this, we'll use two targeting specs from JSON files. 76 | #' 77 | 78 | ts1 <- read_file("targeting_specs/targeting_spec_01.json") 79 | ts2 <- read_file("targeting_specs/targeting_spec_02.json") 80 | 81 | #' ## 82 | cat(ts1) 83 | 84 | #' ## Helper function 85 | #' 86 | #' Each request returns a single number. To build an interesting 87 | #' data set, we need to make many requests. 88 | #' 89 | #' To do this, we'll wrap our request code in a *helper function*. 90 | #' The function will take a targeting spec and return a response. 91 | #' The other query parameters that don't change will be hard-coded. 92 | #' 93 | #' Finally, we need to know about the concept of **rate limiting**. 94 | #' If we make too many requests too quickly, we'll be *rate limited*, 95 | #' and we won't be able to make any more requests for a while. 96 | #' 97 | 98 | #' ## 99 | make_fb_ads_request <- function(ts) { 100 | # Avoid rate limiting! 101 | # Don't make too many requests in a short period of time. 102 | Sys.sleep(5) 103 | 104 | fb_query <- list( 105 | access_token = fb_cfg$access_token, 106 | currency = "USD", 107 | optimize_for = "NONE", 108 | targeting_spec = minify(ts) 109 | ) 110 | 111 | GET(fb_ads_url, query = fb_query) 112 | } 113 | 114 | #' ## Make requests 115 | 116 | r1 <- make_fb_ads_request(ts1) 117 | r2 <- make_fb_ads_request(ts2) 118 | 119 | #' ## Data processing 120 | #' 121 | #' We need to combine the request information from the targeting spec 122 | #' with the estimated number of users from the response. 123 | #' 124 | #' As with the requests, we'll do this using a function. 125 | #' 126 | #' This function will return a data frame with a single row. 127 | #' 128 | #' ## 129 | process_fb_response <- function(ts, r) { 130 | ts_df <- 131 | as_data_frame(fromJSON(ts)) %>% 132 | unnest(geo_locations) %>% 133 | summarise_all(function(x) { 134 | ifelse(length(unique(x)) > 1, 135 | list(unique(x)), unique(x)) 136 | }) 137 | 138 | r_df <- 139 | as_data_frame(content(r, as = "parsed")$data) %>% 140 | select(users) 141 | 142 | bind_cols(ts_df, r_df) 143 | } 144 | 145 | #' ## 146 | 147 | bind_rows( 148 | process_fb_response(ts1, r1), 149 | process_fb_response(ts2, r2) 150 | ) 151 | 152 | #' ## Example 3: A more complex query 153 | ts3 <- read_file("targeting_specs/targeting_spec_03.json") 154 | cat(ts3) 155 | 156 | #' ## Understanding the targeting spec 157 | #' 158 | #' How many women in the US *or* Great Britain, between ages 25-55, are 159 | #' 160 | #' - either in a relationship *or* married 161 | #' 162 | #' **AND** 163 | #' 164 | #' - either an undergrad *or* an alum 165 | #' 166 | #' https://developers.facebook.com/docs/marketing-api/targeting-specs 167 | #' 168 | 169 | #' ## 170 | r3 <- make_fb_ads_request(ts3) 171 | prettify(content(r3, as = "text")) 172 | 173 | #' Try out `process_fb_response(ts3, r3)` too! 174 | #' 175 | 176 | #' ## Exercises 177 | #' 178 | #' **Exercise 1:** 179 | #' Pick 3 other countries and report the number of Facebook users in each. 180 | #' Compare these numbers to the actual populations from the World Bank or 181 | #' some other source. 182 | #' 183 | #' Use two-digit country codes for countries: 184 | #' https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 185 | #' 186 | 187 | 188 | 189 | #' **Exercise 2:** 190 | #' Pick another US state and age range, and compare the numbers of men and women. 191 | #' You can look up the key that Facebook uses for each state in the file provided, 192 | #' targeting_spec_us_states.json. 193 | #' 194 | #' You will need to create your own json targeting spec, using the examples 195 | #' provided as a template. It is recommended that you edit json files in 196 | #' RStudio, unless you are using a text editor designed for writing code. 197 | #' 198 | 199 | 200 | 201 | #' ## Exercises 202 | #' 203 | #' **Challenge exercise:** 204 | #' Get the Facebook user population for each US state. 205 | #' Compare these estimates to population estimates from the Census ACS. 206 | #' Note that you'll need to make a separate call to the API for each state, 207 | #' which will take several minutes. 208 | #' 209 | 210 | 211 | -------------------------------------------------------------------------------- /facebook_ads.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | -------------------------------------------------------------------------------- /facebook_ads.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccgilroy/r-estimates-fb-ads/c0b7025a0046309f963768beab210bae849a7d84/facebook_ads.pdf -------------------------------------------------------------------------------- /facebook_ads_intro.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: | 3 | | Introduction: 4 | | Demographic estimates from the Facebook Marketing API 5 | author: "Connor Gilroy" 6 | date: "`r Sys.Date()`" 7 | output: 8 | beamer_presentation: 9 | theme: "metropolis" 10 | latex_engine: xelatex 11 | highlight: pygments 12 | df_print: kable 13 | --- 14 | 15 | # Facebook 16 | 17 | Facebook is the social network par excellence. [As of 2016](http://www.pewinternet.org/2016/11/11/social-media-update-2016/), **79%** of US internet users are on Facebook. 18 | 19 | ![](figures/PI_2016.11.11_Social-Media-Update_0-01.png) 20 | 21 | # In the news... 22 | 23 | Facebook Ads are a locus of concern about discrimination, about "fake news", and so on. 24 | 25 | ![](figures/fb_fail.png) 26 | 27 | # In research 28 | 29 | Using Facebook Ads is cutting edge: 30 | 31 | - Emilio's paper with Ingmar Weber and Krishna Gummadi using it to study stocks of international migrants just came out this month. 32 | ([paper](http://onlinelibrary.wiley.com/doi/10.1111/padr.12102/full), [news](http://www.washington.edu/news/2017/10/12/using-facebook-data-as-a-real-time-census/)) 33 | - J. Nathan Matias and others did an audit this summer of who had access to the 'Pride' reaction. 34 | ([article](https://www.theatlantic.com/technology/archive/2017/06/facebook-pride-reaction/531633/), 35 | [GitHub](https://github.com/natematias/Rainbow-Reaction-Analysis)) 36 | 37 | # The Ads Manager 38 | 39 | The ability to target ads by demographics and interests is at the core of both the potential of and the concerns about Facebook Ads. 40 | 41 | ![](figures/fb_ads_manager_example.png) 42 | 43 | # Example: description 44 | 45 | Even when large proportions of users don't provide a piece of information, you can still see clear geographic patterns. 46 | 47 | ![](figures/fb_prop_map.png) 48 | 49 | # Example: validation 50 | 51 | The, it is possible to compare those patterns to existing data sources, which have their own sets of biases and limitations. 52 | 53 | ![](figures/validation_sexuality_acs.png) 54 | 55 | # Technical overview 56 | 57 | **Authentication:** 58 | 59 | Two components: an *access token* and an *ads account*. 60 | 61 | **Requesting information:** 62 | 63 | You specify the audience you want to reach using a *targeting spec* in a JSON format; you then get an estimate of how many users that specification will reach. 64 | 65 | # Documentation 66 | 67 | The Marketing API: 68 | 69 | https://developers.facebook.com/docs/marketing-apis 70 | 71 | Access: 72 | 73 | https://developers.facebook.com/docs/marketing-api/access 74 | 75 | Targeting: 76 | 77 | https://developers.facebook.com/docs/marketing-api/buying-api/targeting 78 | 79 | # Pythonistas, you're in luck 80 | 81 | Matheus Araujo has written `pySocialWatcher`, a Python module for collecting social data from the Marketing API: https://github.com/maraujo/pySocialWatcher 82 | 83 | It relies on the `requests` package, Python's version of `httr`. 84 | 85 | Facebook also provides a Python SDK for the Marketing API, which is an interface/abstraction to the API: https://github.com/facebook/facebook-python-ads-sdk 86 | 87 | # Using other Facebook APIs from R 88 | 89 | `SocialMediaLab` and `Rfacebook` are packages that provide access to public Facebook pages and other aspects of Facebook's Graph API. 90 | 91 | To use these, you can generate a standard OAuth access token from your app ID and app secret, much like you would do with Twitter. 92 | 93 | -------------------------------------------------------------------------------- /facebook_ads_intro.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccgilroy/r-estimates-fb-ads/c0b7025a0046309f963768beab210bae849a7d84/facebook_ads_intro.pdf -------------------------------------------------------------------------------- /targeting_specs/targeting_spec_00.json: -------------------------------------------------------------------------------- 1 | {"geo_locations": {"countries": ["US"]}} -------------------------------------------------------------------------------- /targeting_specs/targeting_spec_01.json: -------------------------------------------------------------------------------- 1 | { 2 | "geo_locations": { 3 | "regions": [{ 4 | "key": "3890", 5 | "name": "Washington" 6 | }] 7 | }, 8 | "genders": [1], 9 | "age_min": 20, 10 | "age_max": 30 11 | } 12 | -------------------------------------------------------------------------------- /targeting_specs/targeting_spec_02.json: -------------------------------------------------------------------------------- 1 | { 2 | "geo_locations": { 3 | "regions": [{ 4 | "key": "3890", 5 | "name": "Washington" 6 | }] 7 | }, 8 | "genders": [2], 9 | "age_min": 20, 10 | "age_max": 30 11 | } 12 | -------------------------------------------------------------------------------- /targeting_specs/targeting_spec_03.json: -------------------------------------------------------------------------------- 1 | { 2 | "geo_locations": { 3 | "countries": ["US", "GB"] 4 | }, 5 | "genders": [2], 6 | "age_min": 25, 7 | "age_max": 55, 8 | "relationship_statuses": [2, 3], 9 | "education_statuses": [2, 3] 10 | } 11 | -------------------------------------------------------------------------------- /targeting_specs/targeting_spec_us_states.json: -------------------------------------------------------------------------------- 1 | {"geo_locations": {"regions": [{"key": "3843", "name": "Alabama"}]}} 2 | {"geo_locations": {"regions": [{"key": "3844", "name": "Alaska"}]}} 3 | {"geo_locations": {"regions": [{"key": "3845", "name": "Arizona"}]}} 4 | {"geo_locations": {"regions": [{"key": "3846", "name": "Arkansas"}]}} 5 | {"geo_locations": {"regions": [{"key": "3847", "name": "California"}]}} 6 | {"geo_locations": {"regions": [{"key": "3848", "name": "Colorado"}]}} 7 | {"geo_locations": {"regions": [{"key": "3849", "name": "Connecticut"}]}} 8 | {"geo_locations": {"regions": [{"key": "3850", "name": "Delaware"}]}} 9 | {"geo_locations": {"regions": [{"key": "3852", "name": "Florida"}]}} 10 | {"geo_locations": {"regions": [{"key": "3853", "name": "Georgia"}]}} 11 | {"geo_locations": {"regions": [{"key": "3854", "name": "Hawaii"}]}} 12 | {"geo_locations": {"regions": [{"key": "3855", "name": "Idaho"}]}} 13 | {"geo_locations": {"regions": [{"key": "3856", "name": "Illinois"}]}} 14 | {"geo_locations": {"regions": [{"key": "3857", "name": "Indiana"}]}} 15 | {"geo_locations": {"regions": [{"key": "3858", "name": "Iowa"}]}} 16 | {"geo_locations": {"regions": [{"key": "3859", "name": "Kansas"}]}} 17 | {"geo_locations": {"regions": [{"key": "3860", "name": "Kentucky"}]}} 18 | {"geo_locations": {"regions": [{"key": "3861", "name": "Louisiana"}]}} 19 | {"geo_locations": {"regions": [{"key": "3862", "name": "Maine"}]}} 20 | {"geo_locations": {"regions": [{"key": "3863", "name": "Maryland"}]}} 21 | {"geo_locations": {"regions": [{"key": "3864", "name": "Massachusetts"}]}} 22 | {"geo_locations": {"regions": [{"key": "3865", "name": "Michigan"}]}} 23 | {"geo_locations": {"regions": [{"key": "3866", "name": "Minnesota"}]}} 24 | {"geo_locations": {"regions": [{"key": "3867", "name": "Mississippi"}]}} 25 | {"geo_locations": {"regions": [{"key": "3868", "name": "Missouri"}]}} 26 | {"geo_locations": {"regions": [{"key": "3869", "name": "Montana"}]}} 27 | {"geo_locations": {"regions": [{"key": "3870", "name": "Nebraska"}]}} 28 | {"geo_locations": {"regions": [{"key": "3871", "name": "Nevada"}]}} 29 | {"geo_locations": {"regions": [{"key": "3872", "name": "New Hampshire"}]}} 30 | {"geo_locations": {"regions": [{"key": "3873", "name": "New Jersey"}]}} 31 | {"geo_locations": {"regions": [{"key": "3874", "name": "New Mexico"}]}} 32 | {"geo_locations": {"regions": [{"key": "3875", "name": "New York"}]}} 33 | {"geo_locations": {"regions": [{"key": "3876", "name": "North Carolina"}]}} 34 | {"geo_locations": {"regions": [{"key": "3877", "name": "North Dakota"}]}} 35 | {"geo_locations": {"regions": [{"key": "3878", "name": "Ohio"}]}} 36 | {"geo_locations": {"regions": [{"key": "3879", "name": "Oklahoma"}]}} 37 | {"geo_locations": {"regions": [{"key": "3880", "name": "Oregon"}]}} 38 | {"geo_locations": {"regions": [{"key": "3881", "name": "Pennsylvania"}]}} 39 | {"geo_locations": {"regions": [{"key": "3882", "name": "Rhode Island"}]}} 40 | {"geo_locations": {"regions": [{"key": "3883", "name": "South Carolina"}]}} 41 | {"geo_locations": {"regions": [{"key": "3884", "name": "South Dakota"}]}} 42 | {"geo_locations": {"regions": [{"key": "3885", "name": "Tennessee"}]}} 43 | {"geo_locations": {"regions": [{"key": "3886", "name": "Texas"}]}} 44 | {"geo_locations": {"regions": [{"key": "3887", "name": "Utah"}]}} 45 | {"geo_locations": {"regions": [{"key": "3888", "name": "Vermont"}]}} 46 | {"geo_locations": {"regions": [{"key": "3889", "name": "Virginia"}]}} 47 | {"geo_locations": {"regions": [{"key": "3890", "name": "Washington"}]}} 48 | {"geo_locations": {"regions": [{"key": "3851", "name": "Washington, District of Columbia"}]}} 49 | {"geo_locations": {"regions": [{"key": "3891", "name": "West Virginia"}]}} 50 | {"geo_locations": {"regions": [{"key": "3892", "name": "Wisconsin"}]}} 51 | {"geo_locations": {"regions": [{"key": "3893", "name": "Wyoming"}]}} 52 | --------------------------------------------------------------------------------