10 Free APIs for Python — Commercial Use, Production Code & Safety Guide

With external APIs, Python developers can implement data retrieval, financial information, and image processing features without building everything from scratch. However, “free to use” and “safe to use in production” are two entirely different things.

This article introduces 10 practical free APIs for Python with production-ready code, Rate Limit details, and safety design patterns. Beyond a simple API list, we cover the design philosophy professionals use when working with external APIs.

Target level: Beginner to intermediate (able to use requests). Prerequisite:

Bash
pip install requests

For secure API key management, also see “10 Python Security Implementation Patterns.”

Basic API Call Template

Before diving into individual APIs, let’s establish the baseline template that every API call should follow. In production, these four points are the minimum standard:

  • Set a timeout — prevent your program from waiting indefinitely
  • Check status codes — handle non-200 responses properly
  • Handle exceptions — cope with network failures and timeouts
  • Don’t assume JSON — avoid crashes when the response is HTML or text
basic_template.py
import requests

url = "https://api.example.com/data"

try:
    response = requests.get(url, timeout=5)
    response.raise_for_status()
    data = response.json()
    print(data)
except requests.exceptions.Timeout:
    print("Timeout: server not responding")
except requests.exceptions.HTTPError as e:
    print(f"HTTP error: {e.response.status_code}")
except requests.exceptions.RequestException as e:
    print(f"Request error: {e}")
except ValueError:
    print("JSON decode error: response is not JSON")
💡 Tip

Keep this template as a shared utility function in your project. The code examples below omit error handling for brevity, but always apply this pattern in production code.

Free API Comparison Table

APIPurposeAPI KeyRate LimitCommercialDifficulty
① Open-MeteoWeather dataNoneRelaxedYes★☆☆
② ExchangeRateForex ratesNone1,500/dayYes★☆☆
③ CoinGeckoCrypto pricesNone10–30/minCheck ToS★☆☆
④ JSONPlaceholderTest/MockNoneNone★☆☆
⑤ REST CountriesCountry infoNoneRelaxedYes★☆☆
⑥ The Cat APICat imagesOptional10/minYes★☆☆
⑦ IP-APIIP geolocationNone45/minPaid only★☆☆
⑧ Advice SlipRandom quotesNone1/2secYes★☆☆
⑨ PokeAPIPokémon dataNoneRelaxedYes★★☆
⑩ Numbers APINumber triviaNoneRelaxedYes★☆☆

① Open-Meteo — No-Key Weather API

Open-Meteo is a completely free, no-API-key weather data API. It provides current weather, temperature, wind speed, precipitation, and forecasts up to 16 days ahead. Commercial use is permitted, making it the go-to choice for weather-related tools.

open_meteo.py
import requests

url = "https://api.open-meteo.com/v1/forecast"
params = {
    "latitude": 35.6762,
    "longitude": 139.6503,
    "current_weather": True
}

response = requests.get(url, params=params, timeout=5)
data = response.json()

weather = data["current_weather"]
print(f"Temp: {weather['temperature']}°C")
print(f"Wind: {weather['windspeed']} km/h")

The biggest advantage is instant data access without any authentication. No API key registration or account creation needed. Change the latitude/longitude and you get weather data from anywhere in the world.

💡 Tip

Open-Meteo is an open-source project using data from European meteorological agencies. Accuracy is high even for locations in Asia and the Americas, making it suitable for both personal projects and commercial tools.

② ExchangeRate API — Forex Rates

ExchangeRate API provides free foreign exchange rates for major currencies. Useful for currency conversion tools, price comparison sites, and financial dashboards. No API key required, with 1,500 requests per day.

exchange_rate.py
import requests

url = "https://open.er-api.com/v6/latest/USD"
response = requests.get(url, timeout=5)
data = response.json()

jpy_rate = data["rates"]["JPY"]
print(f"1 USD = {jpy_rate} JPY")

# Currency conversion example
usd_amount = 100
jpy_amount = usd_amount * jpy_rate
print(f"{usd_amount} USD = {jpy_amount:.0f} JPY")

A critical consideration with financial data is update frequency. This API updates roughly once per day, so it’s unsuitable for real-time trading. It provides sufficient accuracy for approximate currency conversion and daily price references.

⚠️ Note

When displaying exchange rates to users, always state “rates are approximate.” Discrepancies with real-time rates are possible, and this data should not be used as the basis for financial transactions.

③ CoinGecko API — Crypto Prices & Market Data

CoinGecko provides cryptocurrency prices, market caps, trading volumes, and rankings. The free plan is surprisingly powerful and widely used for crypto-related tool development.

coingecko.py
import requests

url = "https://api.coingecko.com/api/v3/simple/price"
params = {
    "ids": "bitcoin,ethereum",
    "vs_currencies": "usd,eur",
    "include_24hr_change": "true"
}

response = requests.get(url, params=params, timeout=5)
data = response.json()

btc = data["bitcoin"]
print(f"BTC: ${btc['usd']:,.0f} (24h: {btc['usd_24h_change']:.1f}%)")

CoinGecko’s Rate Limit on the free plan is roughly 10–30 requests per minute. Since crypto prices are volatile, caching is essential. Fetch data every minute, store locally, and serve from cache.

💡 Tip

CoinGecko supports fetching multiple currencies in a single request. Use ids=bitcoin,ethereum,solana with comma separation to dramatically reduce request count.

④ JSONPlaceholder — The Standard for API Practice

JSONPlaceholder is a mock API designed for REST API learning and testing. It provides simulated data for users, posts, comments, and albums, widely used for API development prototyping and frontend testing.

jsonplaceholder.py
import requests

# Get posts
url = "https://jsonplaceholder.typicode.com/posts"
params = {"_limit": 3}
response = requests.get(url, params=params, timeout=5)
posts = response.json()

for post in posts:
    print(f"[{post['id']}] {post['title']}")

# Practice POST requests
new_post = {"title": "Test", "body": "Hello", "userId": 1}
res = requests.post(url, json=new_post, timeout=5)
print(f"Created: status={res.status_code}")

The biggest advantage is support for all HTTP methods: GET, POST, PUT, PATCH, DELETE. POST and DELETE requests don’t modify actual data — responses are simulated, making it safe to experiment. Ideal for onboarding new engineers and testing API clients.

💡 Tip

If you need a mock API for your own project, use JSONPlaceholder’s design as reference and set up a local mock server with json-server (npm package).

⑤ REST Countries — Country Information at a Glance

REST Countries provides basic information about every country in the world — population, area, capital, flag, currency, languages, and region — all in JSON format. Great for geography education tools, statistics dashboards, and internationalized applications.

rest_countries.py
import requests

url = "https://restcountries.com/v3.1/name/japan"
response = requests.get(url, timeout=5)
data = response.json()[0]

print(f"Country: {data['name']['common']}")
print(f"Capital: {data['capital'][0]}")
print(f"Population: {data['population']:,}")
print(f"Region: {data['region']}")
print(f"Flag: {data['flag']}")

This API is ideal for caching since country data rarely changes. Fetch all data once and store locally — no need to call the API again. Loading all data into memory at startup is the most efficient approach.

💡 Tip

The /v3.1/all endpoint returns data for all 250+ countries at once. Use field filters (?fields=name,population,capital) to reduce response size and save bandwidth.

⑥ The Cat API — Random Cat Images

The Cat API returns random cat images. While it seems playful, it’s genuinely useful in production for UI image display testing, dynamic placeholder generation, and API client testing.

cat_api.py
import requests

url = "https://api.thecatapi.com/v1/images/search"
params = {"limit": 3}
response = requests.get(url, params=params, timeout=5)
images = response.json()

for img in images:
    print(f"URL: {img['url']} (size: {img['width']}x{img['height']})")

Basic features work without an API key, but registering for a free key increases rate limits and unlocks breed filters and favorites.

💡 Tip

When testing “dynamic image display” features in frontend development, this API provides a more realistic test environment than static dummy images.

⑦ IP-API — IP Geolocation

IP-API returns geolocation data from IP addresses — country, region, city, ISP, and timezone. Used for access analytics, region-based content display, and security monitoring.

ip_api.py
import requests

url = "http://ip-api.com/json/"
response = requests.get(url, timeout=5)
data = response.json()

print(f"Country: {data['country']}")
print(f"Region: {data['regionName']}")
print(f"City: {data['city']}")
print(f"ISP: {data['isp']}")
print(f"Timezone: {data['timezone']}")
⚠️ Important

The free tier is HTTP only (HTTPS requires a paid plan). For production environments requiring HTTPS, consider upgrading or switching to alternatives like ipinfo.io. Commercial use also requires the paid plan.

Rate limit is 45 requests per minute. For batch IP lookups, use the http://ip-api.com/batch endpoint for better efficiency.

⑧ Advice Slip API — Random Quotes

Advice Slip API returns random advice and quotes in English. Useful for UI test displays, daily message features, and chatbot embellishments.

advice_slip.py
import requests

url = "https://api.adviceslip.com/advice"
response = requests.get(url, timeout=5)
data = response.json()

advice = data["slip"]["advice"]
print(f"Today's advice: {advice}")

This API features a minimalist endpoint design — just /advice (random) and /advice/{id} (by ID). A 2-second interval limit applies between consecutive calls.

💡 Tip

APIs with “one endpoint, one response” designs like this serve as textbook references for API design. When building your own API, starting this simple is a production best practice.

⑨ PokeAPI — Perfect for Learning Structured Data

PokeAPI provides Pokémon names, types, stats, and images. Beyond game data, its structure makes it excellent for learning nested JSON processing, pagination, and relational data handling — patterns you’ll encounter in every production API.

pokeapi.py
import requests

url = "https://pokeapi.co/api/v2/pokemon/pikachu"
response = requests.get(url, timeout=5)
data = response.json()

print(f"Name: {data['name']}")
print(f"Type: {data['types'][0]['type']['name']}")
print(f"HP: {data['stats'][0]['base_stat']}")
print(f"Image: {data['sprites']['front_default']}")

PokeAPI responses are deeply nested, requiring access patterns like data["types"][0]["type"]["name"]. This is common in production APIs (Stripe, Twilio, AWS SDK), so practicing here makes real-world API integration smoother.

💡 Tip

PokeAPI has 1,000+ entries with pagination support (?offset=20&limit=20). The list → detail pattern directly applies to e-commerce and social media API design.

⑩ Numbers API — Number Trivia

Numbers API returns trivia and fun facts about any number. Useful for educational sites, quiz tools, and UI embellishment with daily content.

numbers_api.py
import requests

number = 42
url = f"http://numbersapi.com/{number}?json"
response = requests.get(url, timeout=5)
data = response.json()

print(f"{number}: {data['text']}")
# e.g.: 42: 42 is the answer to the Ultimate Question ...

Types include trivia (default), math, date, and year. For example, http://numbersapi.com/3/14/date?json returns trivia about March 14th.

⚠️ Note

Numbers API is HTTP only — no HTTPS support. For production use, either avoid it or relay requests through your server and return HTTPS responses to clients.

Rate Limits and Caching Strategy

The most dangerous pitfall with free APIs is exceeding rate limits. Free services have strict limits, and violations result in IP bans that can last hours or days.

Typical rate limit patterns:

PatternExampleUse Case
Per second1/secReal-time data
Per minute60/minStandard APIs
Per day1,000/dayData retrieval

The solution is caching. Store API responses locally and serve from cache within a TTL window.

cache_example.py
import requests
import time
import json
import os

CACHE_FILE = "cache.json"
CACHE_TTL = 300  # 5 minutes

def get_with_cache(url):
    if os.path.exists(CACHE_FILE):
        with open(CACHE_FILE) as f:
            cache = json.load(f)
        if url in cache and time.time() - cache[url]["time"] < CACHE_TTL:
            return cache[url]["data"]

    response = requests.get(url, timeout=5)
    data = response.json()

    cache = {}
    if os.path.exists(CACHE_FILE):
        with open(CACHE_FILE) as f:
            cache = json.load(f)
    cache[url] = {"data": data, "time": time.time()}
    with open(CACHE_FILE, "w") as f:
        json.dump(cache, f)

    return data
💡 Tip

In production, use Redis or memcached instead of file caching. Django provides django.core.cache, and FastAPI has aiocache for built-in caching support.

Essential Safety Design Patterns

Production code using external APIs requires these five design patterns:

PatternPurposeRisk if Missing
timeoutPrevent infinite waitProgram freezes
retryHandle transient failuresProcessing halts
rate controlRespect API limitsIP ban
fallbackHandle API outagesService down
cacheReduce requestsLimit exceeded

A combined production pattern:

safe_api_call.py
import requests
import time

def safe_api_call(url, max_retries=3, delay=1):
    for attempt in range(max_retries):
        try:
            response = requests.get(url, timeout=5)
            if response.status_code == 429:  # Rate Limit
                wait = int(response.headers.get("Retry-After", delay * 2))
                time.sleep(wait)
                continue
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException:
            if attempt < max_retries - 1:
                time.sleep(delay * (attempt + 1))
                continue
            return None  # fallback
    return None

Remember this production principle: external APIs are unreliable by default. Outages, spec changes, latency spikes, limit changes — these aren’t “might happen” scenarios, they’re “will happen.” Professionals design with the assumption that external APIs cannot be trusted.

For more on secure design, see “10 Python Security Implementation Patterns.”

Common Mistakes and Terms of Service

Know the failure patterns beginners commonly hit:

MistakeResultSolution
API calls in a loopIP bansleep + cache
No cachingRate limit exceededFile/Redis cache
No error handlingProgram crashtry/except required
No timeoutProgram freezestimeout=5 as standard
Ignoring ToSLegal riskAlways read Terms

Always check the Terms of Service. Even free APIs may have restrictions:

  • No commercial use — personal use only
  • Attribution required — “Powered by ○○” display obligation
  • No redistribution — cannot republish raw data on another service
  • HTTPS restrictions — free tier HTTP only (IP-API, Numbers API)
⚠️ Warning

“Free” does not mean “use however you want.” Ignoring Terms of Service is more dangerous than shipping buggy code — it’s a direct legal risk.

Professional Design — Provider Abstraction

Experienced developers always think about external APIs this way:

  • Free APIs are for prototyping and validation
  • Plan for paid tier migration or self-hosted replacement in production
  • Design to avoid over-dependence on any single provider

This is achieved through Provider Abstraction — wrapping API calls behind an interface so providers can be swapped without changing application code.

provider_abstraction.py
class WeatherProvider:
    def get_temperature(self, lat, lon):
        raise NotImplementedError

class OpenMeteoProvider(WeatherProvider):
    def get_temperature(self, lat, lon):
        url = f"https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lon}&current_weather=true"
        data = requests.get(url, timeout=5).json()
        return data["current_weather"]["temperature"]

class PaidWeatherProvider(WeatherProvider):
    def get_temperature(self, lat, lon):
        # Swap to paid API by changing just this class
        pass

# Usage
weather = OpenMeteoProvider()
temp = weather.get_temperature(35.67, 139.65)
print(f"Temperature: {temp}°C")

With this design, switching API providers requires changing just one class. No need to rewrite your entire application. During testing, you can swap in a mock provider as well.

💡 Tip

This is also known as the “Strategy Pattern” and applies beyond APIs to databases, email services, file storage — any external service integration.

FAQ

Q: What’s the difference between APIs with and without keys?

API keys identify users. Keyless APIs are convenient but tend to have stricter IP-based rate limits. APIs with keys can manage limits per user and typically allow more requests.

Q: Can I build a public service using free APIs?

It depends on the Terms of Service. Open-Meteo and REST Countries allow commercial use, but IP-API’s free tier is non-commercial only. Always verify each API’s ToS before publishing.

Q: What if an API suddenly goes down?

This is exactly why fallback design matters. Keep recent data in cache so you can serve it during outages. For prolonged downtime, use Provider Abstraction to switch to an alternative API.

Q: What does 429 Too Many Requests mean?

You’ve exceeded the rate limit. Check the Retry-After response header and wait the specified seconds before retrying. If you’re consistently hitting 429, implement caching or increase request intervals.

Conclusion

Free APIs let Python developers implement weather, forex, crypto, image, and countless other features in record time. But what truly matters is not which APIs you choose — it’s how you use them.

  1. Always implement timeout and error handling
  2. Check Rate Limits and stay within them using caching
  3. Read the Terms of Service before using any API
  4. Manage dependencies with Provider Abstraction
  5. Design with the assumption that external APIs are unreliable

APIs are powerful, but over-dependence breaks things. Used wisely, they can dramatically accelerate your development speed. That’s the essence of external API integration.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *