Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
275: Handling JSON APIs with requests
I've been wanting to build a small tool to track some gaming stats, and that means I need to get comfortable talking to external APIs. Most of the web speaks JSON these days, and while we've touched on dictionaries and lists, actually pulling that data over the wire is where things usually get messy. Let's dive in.
Wait, where is my data?
I'll start with something simple. I want to get some data about Pikachu from the PokeAPI. I've got the requests library installed, so I'll just fire off a get request and see what happens.
import requests
response = requests.get('https://pokeapi.co/api/v2/pokemon/pikachu')
print(response)
If you run this, you won't see Pikachu's height or weight. You'll see <Response [200]>. This is a common point of confusion. The requests.get() method doesn't return the data itself; it returns a Response object. The [200] is just the HTTP status code telling us "Okay, everything went fine." The actual data is tucked away inside that object.
Strings aren't dictionaries
Naturally, my first instinct is to just print the response to see what's inside. I'll try accessing the .text attribute, which gives me the raw body of the response.
print(response.text)
This works, but it's a nightmare. It prints a massive, unformatted wall of text. Now, I know this is JSON, which looks exactly like a Python dictionary, but response.text is just one giant string. If I try to do something like response.text['name'], Python is going to throw a TypeError because you can't use a string key to index into a string.
The .json() shortcut
I could use the built-in json module to parse that string, but requests has a much cleaner way to handle this. There's a built-in method called .json() that does the heavy lifting for us.
data = response.json()
print(data['name']) # Output: pikachu
print(data['weight']) # Output: 60
Much better. By calling .json(), the library takes that raw string, parses it, and converts it directly into a Python dictionary (or a list, depending on the API). Now I can navigate the data using the standard key-lookup syntax we've used before. I noticed the API nests its data quite deeply, though. If I want to find Pikachu's first ability, I have to chain my lookups:
# Abilities is a list, so we grab the first element [0],
# then the 'ability' dictionary, then the 'name' key.
first_ability = data['abilities'][0]['ability']['name']
print(f"Pikachu's primary ability is {first_ability}.")
When the API says "No"
So far, everything is sunshine and rainbows because I'm using a valid URL. But in a real app, things break. What happens if I search for a Pokémon that doesn't exist?
response = requests.get('https://pokeapi.co/api/v2/pokemon/not-a-real-pokemon')
data = response.json() # This might crash or return an error message
The problem here is that requests doesn't automatically raise an exception if the server returns a 404 (Not Found) or a 500 (Server Error). It just gives you the response object with that error code. If I call .json() on a 404 page that happens to return HTML instead of JSON, my program will crash with a JSONDecodeError.
To fix this, I'll use raise_for_status(). This method checks the status code and immediately throws an exception if the request wasn't successful. It's a much cleaner way to handle errors than writing if response.status_code == 200: every single time.
try:
response = requests.get('https://pokeapi.co/api/v2/pokemon/not-a-real-pokemon')
response.raise_for_status()
data = response.json()
except requests.exceptions.HTTPError as err:
print(f"HTTP error occurred: {err}")
except Exception as err:
print(f"An unexpected error occurred: {err}")
Now the code is robust. It tries to get the data, ensures the server actually found the resource, and handles the failure gracefully if it didn't.
📋 Practical Task
Build a Pokémon Ability Explorer
Your task is to write a script that allows a user to input the name of a Pokémon and prints out a formatted list of all its abilities.
Requirements:
- Prompt the user for a Pokémon name (e.g., "charizard" or "ditto").
- Use
requests.get()to fetch data fromhttps://pokeapi.co/api/v2/pokemon/{name}. - Implement
response.raise_for_status()within atry/exceptblock to handle cases where the user enters a name that doesn't exist. - Parse the JSON response and loop through the
abilitieslist to print each ability's name in a clean format (e.g., "Ability: Blaze"). - If an error occurs, print a user-friendly message explaining that the Pokémon could not be found.
There are no comments for now.