Python · API · Beginner

Redirects

5 tasks

Understand HTTP redirects, inspect redirect chains, and control whether requests follows them.

How HTTP redirects work

#
When you request a URL, the server might respond with a redirect instead of data — a signal that says 'the resource lives somewhere else now, go there instead.' Two redirect status codes matter most: - 301 Moved Permanently — the URL has changed forever. Browsers and scripts should update their bookmarks. - 302 Found (temporary redirect) — the resource is temporarily at another URL. Keep using the original URL next time. Both codes come with a Location header that tells the client where to go next. The requests library follows redirects automatically by default. When you get a response back, you have already arrived at the final destination: ```python import requests BASE_URL = 'https://apilearn.tukas.dev' response = requests.get(f'{BASE_URL}/api/redirect/') print(response.status_code) # 200 — final destination print(response.url) # https://apilearn.tukas.dev/api/products/ ``` To see the redirect itself instead of the final response, pass allow_redirects=False: ```python response = requests.get(f'{BASE_URL}/api/redirect/', allow_redirects=False) print(response.status_code) # 302 print(response.headers['Location']) # where it redirects to ``` When requests follows a redirect chain, it stores the intermediate responses in response.history — a list of Response objects, one per hop: ```python response = requests.get(f'{BASE_URL}/api/redirect/') for hop in response.history: print(hop.status_code, hop.url) # 302 https://apilearn.tukas.dev/api/redirect/ print(response.status_code, response.url) # 200 final URL ``` response.history is empty when there were no redirects. response.url always shows the final URL you actually landed on, regardless of how many hops it took.

Working with redirects in practice

#
Compare a temporary and a permanent redirect: ```python import requests BASE_URL = 'https://apilearn.tukas.dev' # Temporary redirect (302) r302 = requests.get(f'{BASE_URL}/api/redirect/', allow_redirects=False) print(r302.status_code) # 302 print(r302.headers['Location']) # /api/products/ # Permanent redirect (301) r301 = requests.get(f'{BASE_URL}/api/redirect/permanent/', allow_redirects=False) print(r301.status_code) # 301 print(r301.headers['Location']) # /api/products/ ``` Both go to the same place — the difference is in the status code and what it signals to clients. Inspect a chain of redirects with response.history: ```python response = requests.get(f'{BASE_URL}/api/redirect/chain/3/') print(f'Hops: {len(response.history)}') for i, hop in enumerate(response.history, 1): print(f' {i}. {hop.status_code} {hop.url}') print(f'Final: {response.status_code} {response.url}') ``` Each step in the chain is a separate redirect. The /api/redirect/chain/{steps}/ endpoint produces exactly N intermediate hops before landing on /api/products/. Why would you ever disable redirect following? A few real-world reasons: - You want to inspect the Location header before following it. - You need to detect whether a URL redirects at all (e.g. URL validation). - You are implementing a crawler and want explicit control over which redirects to follow. - You are testing redirect behavior in an application you built. For day-to-day data fetching, the default (allow_redirects=True) is almost always what you want.

Redirect reference

#
Redirect endpoints: ``` GET /api/redirect/ 302 → /api/products/ GET /api/redirect/permanent/ 301 → /api/products/ GET /api/redirect/chain/{n}/ n redirects (302 each) → /api/products/ n must be 1–10 ``` Redirect-related response attributes: ``` response.status_code Final status code (200 when auto-followed) response.url Final URL you landed on response.history List of intermediate Response objects (empty if no redirect) response.history[0].url URL of the first redirect ``` Disable automatic following: ```python response = requests.get(url, allow_redirects=False) # response.status_code is now 301 or 302 # response.headers['Location'] holds the redirect target ``` Common status codes for redirects: ``` 301 Moved Permanently URL changed forever; update stored links 302 Found Temporary move; keep original URL ```
01

Follow a redirect

#

Send a GET request to /api/redirect/ (with default redirect following). Print the final status code and the URL you landed on.

import requests

BASE_URL = 'https://apilearn.tukas.dev'

# Follow the redirect and print status_code and final URL
Solution
import requests

BASE_URL = 'https://apilearn.tukas.dev'

response = requests.get(f'{BASE_URL}/api/redirect/')
print(response.status_code)
print(response.url)
02

See the raw redirect

#

Send a GET to /api/redirect/ with allow_redirects=False. Print the status code and the Location header.

import requests

BASE_URL = 'https://apilearn.tukas.dev'

# Disable redirect following and inspect the redirect response
Solution
import requests

BASE_URL = 'https://apilearn.tukas.dev'

response = requests.get(f'{BASE_URL}/api/redirect/', allow_redirects=False)
print(response.status_code)
print(response.headers['Location'])
03

Inspect the redirect history

#

Send a GET to /api/redirect/ and print all intermediate responses from response.history. For each, print the status code and URL. Then print the final status code and URL.

import requests

BASE_URL = 'https://apilearn.tukas.dev'

response = requests.get(f'{BASE_URL}/api/redirect/')

# Print each hop in response.history, then the final response
Solution
import requests

BASE_URL = 'https://apilearn.tukas.dev'

response = requests.get(f'{BASE_URL}/api/redirect/')

for hop in response.history:
    print(hop.status_code, hop.url)
print(response.status_code, response.url)
04

Count hops in a chain

#

Send a GET to /api/redirect/chain/5/ and print how many redirects occurred. Then print the URL of each hop in the chain.

import requests

BASE_URL = 'https://apilearn.tukas.dev'

response = requests.get(f'{BASE_URL}/api/redirect/chain/5/')

# Print number of hops and each URL in the chain
Solution
import requests

BASE_URL = 'https://apilearn.tukas.dev'

response = requests.get(f'{BASE_URL}/api/redirect/chain/5/')

print(f'Hops: {len(response.history)}')
for hop in response.history:
    print(hop.url)
05

Compare redirect types

#

Compare /api/redirect/ and /api/redirect/permanent/ side by side. For each, use allow_redirects=False and print the status code.

import requests

BASE_URL = 'https://apilearn.tukas.dev'

# Check status codes of both redirect types without following
Solution
import requests

BASE_URL = 'https://apilearn.tukas.dev'

r_temp = requests.get(f'{BASE_URL}/api/redirect/', allow_redirects=False)
r_perm = requests.get(f'{BASE_URL}/api/redirect/permanent/', allow_redirects=False)

print(f'/api/redirect/           → {r_temp.status_code}')
print(f'/api/redirect/permanent/ → {r_perm.status_code}')