diff --git a/1-foundations/4-api-and-dataviz/Introduction to APIs.pdf b/1-foundations/4-api-and-dataviz/Introduction to APIs.pdf index 66aaa8f..c399fa9 100644 Binary files a/1-foundations/4-api-and-dataviz/Introduction to APIs.pdf and b/1-foundations/4-api-and-dataviz/Introduction to APIs.pdf differ diff --git a/1-foundations/4-api-and-dataviz/Introduction to APIs.pptx b/1-foundations/4-api-and-dataviz/Introduction to APIs.pptx index 9078360..6a32f50 100644 Binary files a/1-foundations/4-api-and-dataviz/Introduction to APIs.pptx and b/1-foundations/4-api-and-dataviz/Introduction to APIs.pptx differ diff --git a/1-foundations/4-api-and-dataviz/foundations-s4-api-bonus.ipynb b/1-foundations/4-api-and-dataviz/foundations-s4-api-bonus.ipynb new file mode 100644 index 0000000..798a8c6 --- /dev/null +++ b/1-foundations/4-api-and-dataviz/foundations-s4-api-bonus.ipynb @@ -0,0 +1,288 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import requests" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Argument-based parameters\n", + "\n", + "- The example of geoBoundaries we previously saw takes _URL-based_ API query parameters\n", + "- Many APIs take _argument-based_ query parameters\n", + "- This will be the case every time you have to use query parameters separated by an ampersand symbol (`&`) after a question mark (`?`) in the API endpoint\n", + "- Take this generic example:\n", + "\n", + "`https://api.org/endpoint/?parameter1=value1¶meter2=value2¶meter3=value3`\n", + "\n", + "- Theoretically, it's _possible_ to modify an API URL call using concatenated strings in Python to build argument-based queries" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "endpoint = 'https://api.org/endpoint/'\n", + "p1 = 'parameter1'\n", + "v1 = 'value1'\n", + "url = endpoint+'?'+p1+'='+v1\n", + "print(url)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Then we could use `requests` as usual to obtain a response from this API\n", + "\n", + "```{python}\n", + "requests.get(url)\n", + "```\n", + "\n", + "- However, the convention in Python is to **use the argument `params` of `requests.get()`** to pass argument-based query parameters\n", + "\n", + "```{python}\n", + "parameters = {'parameter1': 'value1'}\n", + "requests.get(endpoint, params=parameters)\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Applied example: the WBG API\n", + "\n", + "To illustrate an example of argument-based parameters, we'll use again the endpoint of the total population API to fetch country population data. Note that this time the parameters `date` and `year` are passed in the dictionary named `parameters` and not in the URL string as we did before." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def fetch_population_by_year(year):\n", + " \n", + " endpoint = 'https://api.worldbank.org/v2/country/all/indicator/SP.POP.TOTL'\n", + " parameters = {'date': year, 'format':'json'}\n", + " # note: the API documentation specifies that format=json\n", + " # is a required parameter in order to return the results as JSON\n", + " response = requests.get(endpoint, params=parameters)\n", + " \n", + " if response.status_code == 200:\n", + " \n", + " data = response.json()\n", + " return data\n", + " \n", + " else:\n", + " \n", + " print('Request failed!')\n", + " return None" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pop_2015 = fetch_population_by_year(2015)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pop_2015" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A few notes:\n", + "- Remember that this is the same data you obtain when accessing https://api.worldbank.org/v2/country/all/indicator/SP.POP.TOTL?date=2015&format=json on a web browser\n", + "- The first element of this list contains metadata about the data returned by the API" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pop_2015[0]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Note the detail of the information in the keys `page`, `pages`, `per_page`, and `total` in the first element of `pop_2015`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print('Total obs: {}'.format(pop_2015[0]['total']))\n", + "print('Obs per page: {}'.format(pop_2015[0]['per_page']))\n", + "print('Total pages: {}'.format(pop_2015[0]['pages']))\n", + "print('Current page: {}'.format(pop_2015[0]['page']))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- The result is only one page with 50 observations, out of a total of 266\n", + "- This means that results are incomplete! More API calls are needed to complete the total 266 observations\n", + "- A further inspection of the API documentation shows that the parameters `page=page_number` or `per_page=obs_per_page` can be used to retrieve complete results.\n", + "\n", + "**Important:** When fetching data using APIs, always inspect the result to look for possible limitations in the results' default format. You might be inadvertently missing observations in your API calls!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Bonus exercise\n", + "\n", + "Modify the function `fetch_population_by_year()` below to retrieve the complete results for a given year (not only page 1) from the population endpoint.\n", + "\n", + "Suggested steps:\n", + "\n", + "1. Run `fetch_population_by_year()` for any year and inspect the resulting list. You will note that the first element of the list is a dictionary with a key `total` that indicates the total number of observations in the query result\n", + "1. In your function, save that number into a variable\n", + "1. Send a new API request adding the parameter `per_page` in the parameters dictionary. Its value should be the total number of observations\n", + "1. The JSON object in the response content will be a new list with the same format: the first element of the list will contain metadata about the API result and the data will be in the second element\n", + "1. Return the second element of your list" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def fetch_population_by_year(year):\n", + " \n", + " endpoint = 'https://api.worldbank.org/v2/country/all/indicator/SP.POP.TOTL'\n", + " parameters = {'date': year, 'format':'json'}\n", + " response = requests.get(endpoint, params=parameters)\n", + " \n", + " if response.status_code == 200:\n", + " \n", + " data = response.json()\n", + " \n", + " # === ADD YOUR SOLUTION HERE ===\n", + " # Note that this time we're not giving you the steps\n", + " # to follow and variables to create. Figuring that\n", + " # out is also part of the exercise :)\n", + " \n", + " return data\n", + " \n", + " else:\n", + " \n", + " print('Request failed!')\n", + " return None" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Coding API clients - More takeaways\n", + "\n", + "- Passing API call parameters as arguments is a more elegant way of coding API clients. Argument-based parameters also work better when introducing non-conventional characters as arguments. Think, for example: how would you include a space character in a URL? argument-based parameters handle those cases seamlessly\n", + "- When authentication is needed, API keys are usually passed as argument-based parameters. If the API has a dedicated client library, they will ask for the key after importing the library (as `user_agent` in `Nominatim()`)\n", + "- Many APIs will combine the use of URL-based and argument-based parameters to pass information in API queries. A good API client will take that into account to build the correct query URL and parameters argument. Take the following example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def fetch_population_by_year_country(year, country):\n", + " \n", + " endpoint = 'https://api.worldbank.org/v2/country/' + country + '/indicator/SP.POP.TOTL'\n", + " parameters = {'date': year, 'format':'json'}\n", + " # note: the API documentation specifies that format=json\n", + " # is a required parameter in order to return the results as JSON\n", + " response = requests.get(endpoint, params=parameters)\n", + " \n", + " if response.status_code == 200:\n", + " \n", + " data = response.json()\n", + " return data\n", + " \n", + " else:\n", + " \n", + " print('Request failed!')\n", + " return None" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "brazil_data = fetch_population_by_year_country(2015, 'BRA')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "brazil_data" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/1-foundations/4-api-and-dataviz/foundations-s4-api.ipynb b/1-foundations/4-api-and-dataviz/foundations-s4-api.ipynb index 60a79f8..bf3e83f 100644 --- a/1-foundations/4-api-and-dataviz/foundations-s4-api.ipynb +++ b/1-foundations/4-api-and-dataviz/foundations-s4-api.ipynb @@ -25,7 +25,7 @@ "source": [ "# Interacting with APIs using Python\n", "\n", - "Now that we have an understanding of APIs, we can start interacting with them programatically." + "Now that we understanding what APIs are, we can start interacting with them programatically." ] }, { @@ -34,7 +34,7 @@ "source": [ "# The `requests` library\n", "\n", - "- `requests` is a Python library to interact with the internet\n", + "- `requests` is a Python library to interact with information from the internet\n", "- It sends and receives data to and from URLs\n", "- You can think of it as a web browser (Chrome, Firefox), but without the graphic interface\n", "- APIs are URLs, so we can use `requests` to interact with APIs in Python\n", @@ -51,6 +51,13 @@ "import requests" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Requests is a library but is always part of \"base\" Python, so you don't have to install it." + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -65,7 +72,7 @@ "- When you access a URL in your web browser, it is sending a request to receive information from a web server\n", "- Usually, most of the information your browser receives as a response to your request is in HTML format\n", "- Your web browser then renders the HTML information in the response and shows it to you\n", - "- A probably familiar example:\n", + "- A familiar example:\n", "\n", "`https://www.worldbank.org/en/home`" ] @@ -381,6 +388,7 @@ " \n", " # === DO NOT MODIFY THE FUNCTION FROM THIS POINT ON ===\n", " else:\n", + " print('Request failed!')\n", " return None " ] }, @@ -409,13 +417,13 @@ "source": [ "# Coding a simple API client\n", "\n", - "- Remember we introduced the term \"client\" in the API explanation? A client is a piece of code that facilitates the interaction with an API\n", + "- An API \"client\" is a piece of code that facilitates the interaction with an API\n", "- In the example with the astronauts, we had to go through several coding steps to execute the request, obtain the JSON data, and load it into a Pandas data frame\n", "- All of those steps could be packed in a Python function that simplified the process of interacting with the API. That function is an API client\n", "- In fact, in exercise 1 you were inadvertently creating an API client!\n", "- Most APIs require custom information to be passed in the URL. This can be incorporated to an API client, as in the examples below\n", "\n", - "## Example 1: URL-based parameters\n", + "## Example: URL-based parameters\n", "\n", "We previously introduced the geoBoundaries API example. After exploring this API and its documentation, we knew that it takes URLs with the following generic form:\n", "\n", @@ -505,213 +513,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Example 2: Argument-based parameters\n", - "\n", - "- The example of geoBoundaries takes _URL-based_ API query parameters\n", - "- Many APIs take _argument-based_ query parameters\n", - "- This will be the case every time you have to use query parameters separated by an ampersand symbol (`&`) after a question mark (`?`) in the API endpoint\n", - "- Take this generic example:\n", - "\n", - "`https://api.org/endpoint/?parameter1=value1¶meter2=value2¶meter3=value3`\n", - "\n", - "- Theoretically, it's _possible_ to modify an API URL call using concatenated strings in Python to build argument-based queries" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "endpoint = 'https://api.org/endpoint/'\n", - "p1 = 'parameter1'\n", - "v1 = 'value1'\n", - "url = endpoint+'?'+p1+'='+v1\n", - "print(url)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "- Then we could use `requests` as usual to obtain a response from this API\n", - "\n", - "```{python}\n", - "requests.get(url)\n", - "```\n", - "\n", - "- However, the convention in Python is to **use the argument `params` of `requests.get()`** to pass argument-based query parameters\n", - "\n", - "```{python}\n", - "parameters = {'parameter1': 'value1'}\n", - "requests.get(endpoint, params=parameters)\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Applied example: the WBG API\n", - "\n", - "The WBG has an extensive API with country indicators, among many other data. We'll use the endpoint of the total population API to fetch country population data.\n", - "\n", - "Documentation and use examples of the WBG API can be found in the [WBG Knowledge Base](https://datahelpdesk.worldbank.org/knowledgebase) and the [Developer Information resources](https://datahelpdesk.worldbank.org/knowledgebase/topics/125589-developer-information)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def fetch_population_by_year(year):\n", - " \n", - " endpoint = 'https://api.worldbank.org/v2/country/all/indicator/SP.POP.TOTL'\n", - " parameters = {'date': year, 'format':'json'}\n", - " # note: the API documentation specifies that format=json\n", - " # is a required parameter in order to return the results as JSON\n", - " response = requests.get(endpoint, params=parameters)\n", - " \n", - " if response.status_code == 200:\n", - " \n", - " data = response.json()\n", - " return data\n", - " \n", - " else:\n", - " \n", - " print('Request failed!')\n", - " return None" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pop_2015 = fetch_population_by_year(2015)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pop_2015" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A few notes:\n", - "- This is the same data you obtain when accessing https://api.worldbank.org/v2/country/all/indicator/SP.POP.TOTL?date=2015&format=json on a web browser\n", - "- In this case, the resulting JSON is not interpreted by Python as a dictionary but a list\n", - "- The first element of this list contains metadata about the data returned by the API. The second element contains the actual data" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pop_2015[0]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "- Note the detail of the information in the keys `page`, `pages`, `per_page`, and `total` in the first element of `pop_2015`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print('Total obs: {}'.format(pop_2015[0]['total']))\n", - "print('Obs per page: {}'.format(pop_2015[0]['per_page']))\n", - "print('Total pages: {}'.format(pop_2015[0]['pages']))\n", - "print('Current page: {}'.format(pop_2015[0]['page']))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "- The result is only one page with 50 observations, out of a total of 266\n", - "- This means that results are incomplete! More API calls are needed to complete the total 266 observations\n", - "- A further inspection of the API documentation shows that the parameters `page=page_number` or `per_page=obs_per_page` can be used to retrieve complete results.\n", - "\n", - "**Important:** When fetching data using APIs, always inspect the result to look for possible limitations in the results' default format. You might be inadvertently missing observations in your API calls!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Coding API clients - Main takeaways 1\n", - "\n", - "- Programming a client for an API requires reviewing the documentation and understanding the API uses\n", - "- Many APIs will combine the use of URL-based and argument-based parameters to pass information in API queries. A good API client will take that into account to build the correct query URL and parameters argument. Take the following example:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def fetch_population_by_year_country(year, country):\n", - " \n", - " endpoint = 'https://api.worldbank.org/v2/country/' + country + '/indicator/SP.POP.TOTL'\n", - " parameters = {'date': year, 'format':'json'}\n", - " # note: the API documentation specifies that format=json\n", - " # is a required parameter in order to return the results as JSON\n", - " response = requests.get(endpoint, params=parameters)\n", - " \n", - " if response.status_code == 200:\n", - " \n", - " data = response.json()\n", - " return data\n", - " \n", - " else:\n", - " \n", - " print('Request failed!')\n", - " return None" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "brazil_data = fetch_population_by_year_country(2015, 'BRA')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "brazil_data" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Coding API clients - Main takeaways 2\n", + "## Coding API clients - Takeaways\n", "\n", + "- Programming a client for an API requires reviewing the documentation, understanding the API uses, and how they fit your needs\n", + "- Many APIs will use several URL-based parameters to pass information in API queries. A good API client will take that into account to build the correct query URL and parameters argument\n", "- Remember that further coding might be needed to get from the API result to the information that is relevant for a user\n", - "- Some APIs divide results with many observations in pages and return only a limited number of pages. It's up to the user to review the results and take measures to ensure data is complete" + "- Some APIs divide results with many observations in pages and return only a limited number of pages. It's up to the user to review the results and take measures to ensure data is complete (check the notebook with bonus content for examples on this)" ] }, { @@ -777,52 +584,6 @@ " return None" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Exercise 2b\n", - "\n", - "Modify the function `fetch_population_by_year()` below to retrieve the complete results for a given year (not only page 1) from the population endpoint.\n", - "\n", - "Suggested steps:\n", - "\n", - "1. Run `fetch_population_by_year()` for any year and inspect the resulting list. You will note that the first element of the list is a dictionary with a key `total` that indicates the total number of observations in the query result\n", - "1. In your function, save that number into a variable\n", - "1. Send a new API request adding the parameter `per_page` in the parameters dictionary. Its value should be the total number of observations\n", - "1. The JSON object in the response content will be a new list with the same format: the first element of the list will contain metadata about the API result and the data will be in the second element\n", - "1. Return the second element of your list" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def fetch_population_by_year(year):\n", - " \n", - " endpoint = 'https://api.worldbank.org/v2/country/all/indicator/SP.POP.TOTL'\n", - " parameters = {'date': year, 'format':'json'}\n", - " response = requests.get(endpoint, params=parameters)\n", - " \n", - " if response.status_code == 200:\n", - " \n", - " data = response.json()\n", - " \n", - " # === ADD YOUR SOLUTION HERE ===\n", - " # Note that this time we're not giving you the steps\n", - " # to follow and variables to create. Figuring that\n", - " # out is also part of the exercise :)\n", - " \n", - " return data\n", - " \n", - " else:\n", - " \n", - " print('Request failed!')\n", - " return None" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -831,7 +592,7 @@ "\n", "- Programming a client for an API is not always needed\n", "- Many APIs have their own client in the form of a Python library\n", - "- Check the example of [`geopy`](https://geopy.readthedocs.io/), a [Nominatim](https://nominatim.org/) encoder" + "- Check the example of [`geopy`](https://geopy.readthedocs.io/), a [Nominatim](https://nominatim.org/) encoder. Nominatim operations are also called \"reverse geocoding\"" ] }, { @@ -948,7 +709,7 @@ " + That's why `Nominatim()` requires the `user_agent` parameter: it's a way of detecting which API calls come from the the same alias\n", " + This is a very soft way of authentication\n", "- When authentication is needed, most APIs will require users to register and account and will provide a unique combination of characters called _API key_ that uniquely identifies the user\n", - "- When they are required, API keys are usually passed as argument-based parameters. If the API has a dedicated client library, they will ask for the key after importing the library (as `user_agent` in `Nominatim()`)" + "- When they are required, API keys are usually passed as parameters. If the API has a dedicated client library, they will ask for the key after importing the library (as `user_agent` in `Nominatim()`)" ] }, { @@ -957,7 +718,70 @@ "source": [ "## The World Bank API Python library\n", "\n", - "- The World Bank API we used for one of the examples above also has a dedicated client Python library\n", + "The WBG has an extensive API with country indicators, among many other data. The following examples uses the endpoint of the total population API to fetch country population data.\n", + "\n", + "Documentation and use examples of the WBG API can be found in the [WBG Knowledge Base](https://datahelpdesk.worldbank.org/knowledgebase) and the [Developer Information resources](https://datahelpdesk.worldbank.org/knowledgebase/topics/125589-developer-information)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def fetch_population_by_year(year):\n", + " \n", + " endpoint = 'https://api.worldbank.org/v2/country/all/indicator/SP.POP.TOTL/'\n", + " url = endpoint + '?date=' + str(year) + '&format=json'\n", + " parameters = {'date': year, 'format':'json'}\n", + " # note: the API documentation specifies that format=json\n", + " # is a required parameter in order to return the results as JSON\n", + " response = requests.get(url)\n", + " \n", + " if response.status_code == 200:\n", + " \n", + " data = response.json()\n", + " return data\n", + " \n", + " else:\n", + " \n", + " print('Request failed!')\n", + " return None" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pop_2015 = fetch_population_by_year(2015)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pop_2015" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A few notes:\n", + "- This is the same data you obtain when accessing https://api.worldbank.org/v2/country/all/indicator/SP.POP.TOTL?date=2015&format=json on a web browser\n", + "- In this case, the resulting JSON is not interpreted by Python as a dictionary but a list\n", + "- The first element of this list contains metadata about the data returned by the API. The second element contains the actual data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Programming this complicated client is not needded at all to interact with the The World Bank API. It has a dedicated client Python library that greatly facilitates its use.\n", "- Release blog post [here](https://blogs.worldbank.org/opendata/introducing-wbgapi-new-python-package-accessing-world-bank-data)\n", "- Documentation [here](https://pypi.org/project/wbgapi/)\n", "- Examples [here](https://nbviewer.org/github/tgherzog/wbgapi/blob/master/examples/wbgapi-cookbook.ipynb)" @@ -985,7 +809,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This example gets the total population of Brazil for all years available:" + "This example gets the total population of Brazil for all years available in a Pandas dataframe:" ] }, { @@ -1001,7 +825,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Remember the endpoint of this API? This URL would have returned a similar result in JSON format:\n", + "This URL would have returned a similar result in JSON format:\n", "https://api.worldbank.org/v2/country/BRA/indicator/SP.POP.TOTL?date=1960:2021&format=json\n", "\n", "We can also get the series for multiple countries if we specify a list instead of a single string:" @@ -1077,7 +901,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -1091,7 +915,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.13" + "version": "3.7.6" } }, "nbformat": 4, diff --git a/1-foundations/4-api-and-dataviz/img/world-bank-site.png b/1-foundations/4-api-and-dataviz/img/world-bank-site.png index ea0b487..3b534eb 100644 Binary files a/1-foundations/4-api-and-dataviz/img/world-bank-site.png and b/1-foundations/4-api-and-dataviz/img/world-bank-site.png differ