(+351) 21 24 10006  ·  info@bconcepts.pt
Carnaxide, Lisbon

How to filter and aggregate data from a REST API in Python: step by step

João Barros 06 de September de 2026 4 min read

This tutorial shows how to filter and aggregate data received from a REST API in Python to obtain summarized, analyzable reports. Knowing how to filter and aggregate directly on the client side is useful when the API does not provide analytical endpoints or when you want to validate data before loading it into an ETL system.

Prerequisites

  • Python 3.8+ installed.
  • Libraries: requests and pandas (pip install requests pandas).
  • A REST API that returns JSON with similar records (e.g., a listing of transactions or events).
  • Text editor/IDE and terminal.

Step 1: understand the API format and choose filters

Before writing code, check the API documentation: query parameters, returned fields and pagination. Decide the filters you will apply (dates, status, type) and the keys to aggregate by (e.g., category, day, customer).

Step 2: fetch API pages with requests

Implement the API call with pagination. Minimal example: request per page until there are no more results. Handle HTTP errors and timeouts.

import requests

def fetch_all(url, params=None, headers=None, page_param='page'):
    params = params.copy() if params else {}
    page = 1
    all_items = []
    while True:
        params[page_param] = page
        resp = requests.get(url, params=params, headers=headers, timeout=10)
        resp.raise_for_status()
        data = resp.json()
        items = data.get('results') or data.get('items') or data
        if not items:
            break
        all_items.extend(items)
        # simple stop condition: when fewer items than a typical page
        if isinstance(items, list) and len(items) < 100:
            break
        page += 1
    return all_items

Step 3: apply filters on the client

Filtering on the client is useful when the API does not accept all parameters. Apply filters in a clear and reproducible way. Example: filter by date and status.

from datetime import datetime

def filter_items(items, start_date=None, end_date=None, status=None):
    def in_range(d):
        if not d:
            return False
        dt = datetime.fromisoformat(d)
        if start_date and dt < start_date:
            return False
        if end_date and dt > end_date:
            return False
        return True

    filtered = []
    for it in items:
        if status and it.get('status') != status:
            continue
        if start_date or end_date:
            if not in_range(it.get('created_at')):
                continue
        filtered.append(it)
    return filtered

Step 4: transform JSON to a pandas DataFrame

Converting to a DataFrame makes aggregation and cleaning easier. Normalize nested lists or optional fields before aggregating.

import pandas as pd

def to_dataframe(items):
    df = pd.json_normalize(items)
    # convert date columns
    if 'created_at' in df.columns:
        df['created_at'] = pd.to_datetime(df['created_at'])
    return df

Step 5: aggregate and compute metrics

Use groupby to summarize by key. Example: sum amounts by day and by category, count transactions and compute average.

def aggregate_metrics(df):
    # create day column
    df['day'] = df['created_at'].dt.date
    # example metrics by day and category
    agg = (df.groupby(['day', 'category'])
             .agg(total_amount=pd.NamedAgg(column='amount', aggfunc='sum'),
                  count=pd.NamedAgg(column='id', aggfunc='count'),
                  avg_amount=pd.NamedAgg(column='amount', aggfunc='mean'))
             .reset_index())
    return agg

Step 6: handle common errors and performance

Common errors: unexpected JSON, timeout, rate limits and missing fields. For large volumes, paginate using the API parameters, process in batches and avoid loading everything into memory. Use chunks and write intermediate results to CSV/Parquet files.

# simple example of writing by chunks
for i in range(0, len(all_items), 1000):
    chunk = all_items[i:i+1000]
    df_chunk = to_dataframe(chunk)
    df_chunk.to_parquet(f'data_chunk_{i//1000}.parquet')

Verify the result

Open the aggregated DataFrame (agg) and confirm sums, counts and dates. Check some raw rows to confirm that the filters worked. Quick verification examples:

print(agg.head())
print(df[['id','created_at','status','amount']].sample(5))
print('Total original:', len(all_items), 'After filters:', len(df))

Conclusion

Filtering and aggregating data from a REST API in Python enables creating reports even when the API lacks analytical endpoints. Next steps: add authentication (Bearer/OAuth), use tools like Dask for large volumes or automate as part of an ETL pipeline. Tip: try storing outputs in Parquet for faster queries — what aggregation would make sense for your team?