PokéFamilyDex

Keep your Pokémon families together


This Python script generates a clean, human-readable National Dex-ordered list of Pokémon grouped by evolutionary family. This is a passion project of mine. I've always wanted to organize my Pokemon Home boxes by Pokemon family, but as far as I can tell there is no resource out there that clealy organizes Pokemon by family. I wanted to create a tool that would allow me to do this, and I hope it will be useful to others as well. The script is designed to be run in a Python environment and outputs the list in a text format that can be easily copied into a document or spreadsheet.

Overview

PokéFamilyDex is a project written in Python that helps organize your Pokémon in a more organic and intuitive way. Rather than relying solely on National Pokédex order, this tool emphasizes grouping by evolutionary family first.

To illustrate the difference, here's an example of a Pokémon HOME box organized purely by National Pokédex order:

An image showing Pokémon HOME boxes organized solely by National Pokédex order
Typical National Pokédex Box Organization

This approach is perfectly valid and widely used. However, PokéFamilyDex organizes Pokémon by evolutionary family first, while still respecting National Pokédex order as a secondary layer. Here's how that same box looks when reorganized using this method:

An image showing Pokémon HOME boxes organized with Family Pokédex order, where evolutionary families are prioritized over National Pokédex number
Pokémon Family Box Sorting

Sorting by evolutionary family offers several advantages over traditional National Pokédex sorting:

  • It groups related Pokémon together, such as placing Pichu next to Pikachu and Raichu—even though Pichu is normally listed later in the Johto section.
  • It includes regional variants and evolutions of older species, such as Alolan Raichu and Mr. Rime, which might otherwise be scattered or omitted when using strict Pokédex numbering.
Background

I've enjoyed Pokémon since I was a kid. I fondly remember going to my friend's house and playing Pokémon Red on his Game Boy Color. Sometimes, he'd let me borrow it—and I'd take it home and play for hours, desperately trying to get as far as I could.

I remember struggling to beat Brock with my Charmander. It made perfect sense to me: with enough heat, rocks should melt—so obviously, my Fire-type Charmander could take down Brock's Onix. Unfortunately, I didn't really understand how type matchups worked yet, and I always hit a wall there.

What intrigued me about Pokémon wasn't the battling—I was never very good at it, and to be honest, I still don't find it all that fun. What I loved was the collection aspect. There was this big, mysterious list of unknown Pokémon out there somewhere, waiting to be discovered. I loved running through tall grass, never knowing what I'd encounter next... it felt magical.

Watching my Pokédex slowly fill up was the most satisfying part of the game for me.

The Impetus

In the summer of 2016, Pokémon GO launched—and like many others, it pulled me right back in. Most people I knew played for a month or two and then moved on. I didn't. Not only did I keep playing, but I also dove back into the mainline games I'd missed over the years.

Back then, there were only 150 Pokémon (or 151, if you counted the playground rumors about Mew hiding under a truck). Now there were over 800—and I was once again hooked.

Over the next few years, I slowly played through Alpha Sapphire, X, and Ultra Moon. Between those three games, I managed to collect almost every available Pokémon—minus a few legendaries and event-only mythicals.

That's when my organizational instincts kicked in. I wanted to put all of them in one place—in a way that felt logical.

My Problem with National Pokédex Order

The most common way to organize Pokémon is by their National Pokédex number, which lists Pokémon chronologically based on the region they were introduced in.

While that makes sense historically, it always bothered me in one specific way: Pokémon families that were expanded in later generations often get split up. One of the clearest examples is the Mr. Mime line:

  • Mime Jr.
  • Mr. Mime
  • Mr. Mime (Galarian Form)
  • Mr. Rime

In National Dex order, this family appears like this:

  • Mr. Mime (Slot 122)
  • Mime Jr. (Slot 439)
  • Mr. Rime (Slot 866)

That splits the family across hundreds of entries and often ignores regional form differences. Galarian Mr. Mime sometimes gets skipped entirely.

Another strong example is the Eevee line. Eevee has a total of eight evolutions, but they're spread across four different generations—so they're rarely displayed together. My script ensures the full evolution family appears in one place:

  • Eevee
  • Vaporeon
  • Jolteon
  • Flareon
  • Espeon
  • Umbreon
  • Leafeon
  • Glaceon
  • Sylveon

The Generation 4 games (Diamond, Pearl, and Platinum) in particular added nearly 30 Pokémon to previously established evolution families, many from the original Red, Green, Blue, and Yellow games. I believe this generation makes the strongest argument for organizing Pokémon by evolutionary family instead of strictly by Pokédex number. Here are just a few of the additions from Gen 4:

  1. Budew
  2. Roserade
  3. Ambipom
  4. Mismagius
  5. Honchkrow
  6. Chingling
  7. Bonsly
  8. Mime Jr.
  9. Happiny
  10. Munchlax
  11. Mantyke
  12. Weavile
  13. Magnezone
  14. Lickilicky
  15. Rhyperior
  16. Tangrowth
  17. Electivire
  18. Magmortar
  19. Togekiss
  20. Yanmega
  21. Leafeon
  22. Glaceon
  23. Gliscor
  24. Mamoswine
  25. Porygon-Z
  26. Gallade
  27. Probopass
  28. Dusknoir
  29. Froslass

Over time, I realized how much I enjoyed seeing my Pokémon organized by their evolutionary families. I began manually sorting my collection in Pokémon HOME this way, but as new games introduced more forms and evolutions, it became harder to keep everything neatly arranged. What started as a fun system quickly turned into a maintenance chore.

Why I Built This

After manually organizing my Pokémon in Pokémon HOME more times than I'd like to admit, I realized: why not automate it? Instead of scrolling through spreadsheets and dragging Pokémon around, I could write a script that grouped them by family — exactly how I wanted — and output a clean, readable list.

This approach is especially helpful for collectors. It's designed to group all members of a family together, even if they were introduced in different generations or have regional forms. No more missing evolutions, skipped forms, or scattered duplicates. It turns the National Dex into something that feels tailored to completionists and box-organizers like me.

My goal with this project was to create a listing that organizes Pokémon based on two priorities:

  1. Evolutionary family comes first
  2. Then, National Pokédex order
Tech Stack
  • Python 3.12 — The entire script is written in modern Python, taking advantage of features like match-case statements and built-in type annotations to keep the code clean and readable. I chose Python because it's flexible, approachable, and highly adaptable. Since this isn't a script that needs to run constantly, performance wasn't a major concern — what mattered more was having something I could easily tweak and adjust as my needs evolved.
  • PokéAPI — A public RESTful API that provides all the evolution chain, species, and form data this script relies on. This project wouldn't be possible without it. The API is impressively well-structured and consistent, and I'm grateful to the maintainers. You can explore it yourself here.
  • Requests Library — Used to fetch data from the PokéAPI. It's a lightweight, no-fuss library that makes working with HTTP requests in Python really simple.
Key Features
  • National Dex + Family-Based Sorting — The script intelligently groups Pokémon by evolutionary family first, then orders those families by National Dex number. This hybrid system keeps related forms and evolutions together, even when they span generations.
  • Accurate Handling of Regional Forms — Pokémon with region-specific forms (like Galarian Mr. Mime or Hisuian Zorua) are included and grouped appropriately, so no variants are accidentally left out.
  • Readable Output Format — The script outputs a clean, plain-text list that's easy to copy into a document or spreadsheet for organizing Pokémon Home boxes or collection trackers.
  • One-Command Simplicity — Run the script once and get a complete, formatted list — no setup or extra tooling needed beyond Python and an internet connection.

Example Output

Once the data is retrieved from PokéAPI, the script generates a plain-text output file. Here's a sample of the formatted results you can expect:

Technical Details
An image showing the technical pipeline of the PokeFamilyDex script
The Technical Pipeline of PokeFamilyDex

The PokeFamilyDex script is built around a simple but effective pipeline: first fetch the raw Pokémon data from PokéAPI, transform it into logical family groupings, and output it in a clean, readable format that prioritizes individual evolution families.

While the goal is straightforward, the process involves several key steps to ensure accuracy—especially when dealing with branching evolutions, regional forms, and other edge cases. Below is a breakdown of the technical flow behind the script:

  1. Fetch all species data

    The script begins by calling the /pokemon-species endpoint from PokéAPI to retrieve metadata for every Pokémon species. This includes the evolution chain URL for each species.

  2. Retrieve and parse evolution chains

    For each evolution chain, the script walks the structure recursively to flatten it into a linear list of Pokémon. This ensures that branched evolutions and multi-stage families remain grouped.

  3. Account for alternate forms

    The script checks the varieties field for alternate forms (e.g., Galarian, Hisuian, Alolan). When one is detected, it modifies the display name to include the form label.

  4. Local file caching

    To avoid making repeated requests to PokéAPI, the script caches API responses as JSON files locally. If a cached version is available, the script uses it instead of calling the API again.

  5. Sort evolutionary families

    Once all evolutionary families are gathered, they are sorted by the lowest National Pokédex number found within each family. This mostly preserves Pokédex order while keeping families grouped.

  6. Handle edge cases

    Certain Pokémon require custom handling due to inconsistencies or missing links in the API data. These are discussed further in the Edge Case Handling section.

  7. Generate the output file

    The final step is writing the output to a .txt file. Each evolutionary family is printed on its own, grouped together and separated from the next. This makes it easy to copy into a document or spreadsheet.

    Example:

Architecture

The architecture of the PokeFamilyDex script is designed with clarity, separation of concerns, and long-term maintainability in mind. While it currently exists as a single-file script, its internal structure reflects modular thinking and careful flow management.

Key Architectural Elements

  • Functional Segmentation

    The script is divided into clearly scoped functions, each with a single responsibility:

    • get_all_species_data() — Fetches all Pokémon species from PokéAPI.
    • get_evolution_chain() — Retrieves the full evolution structure for a given species.
    • flatten_chain() — Recursively walks an evolution chain to produce a linear family list.
    • build_family_list() — Deduplicates and assembles the final ordered output.
  • Centralized Caching

    To avoid unnecessary API calls during repeated runs or debugging, the script caches both species data and evolution chain responses to local JSON files (species_cache.json and evo_cache.json). This dramatically speeds up development and allows for offline testing.

  • Loose Coupling of Data Sources

    Species data and evolution chains are treated as separate, swappable data sources. This makes it easy to supplement or replace PokéAPI in the future—such as adding official sprite URLs, form descriptions, or alternate sources.

  • Priority-Based Sorting

    Once all data is gathered and parsed, the script builds a final list of families using two sorting priorities:

    1. Evolutionary grouping (i.e., all evolutions and regional forms are clustered together).
    2. National Pokédex order (based on the lowest Pokédex number in each family).
  • Minimal Global State

    With the exception of the caches and a few constants, most functions operate independently of shared global variables. This keeps the codebase easier to reason about, test, and refactor.

The architecture reflects the idea that even utility scripts deserve thoughtful structure. While PokeFamilyDex isn't a large-scale application, organizing the logic this way lets me maintain and evolve the project with confidence—especially as more complex branching evolutions, alternate forms, and edge cases are introduced.

Algorithm Design

The heart of this project lies in its ability to determine and preserve Pokémon family groupings in a way that feels natural to collectors. While the logic may seem straightforward at first glance, building an accurate, de-duplicated, and evolution-aware list required some careful planning.

  1. Recursive Traversal of Evolution Chains

    Evolution chains in PokéAPI are structured as nested trees — a base Pokémon evolves into another, which may evolve further, potentially branching along the way. To handle these variable-depth chains, I wrote a recursive function that walks each node and flattens the entire chain into a simple linear list.

    For example, consider the Ralts family. The chain includes split evolutions into either Gardevoir or Gallade. Using a recursive approach ensures that every branch is captured:

    An image showing full evolutionary line of Ralts
    The Ralts evolutionary family

    By recursively calling each child node, the script captures full families — even when they include branching evolutions that wouldn't be visible in a simple linear list.

  2. Preserving Order Without Duplicates

    Since some Pokémon can appear in multiple evolution chains (like Eevee's many forms or branch evolutions), I needed a way to track seen species and ensure each family only appears once in the output. The script uses a set() to store seen species IDs and skips duplicates to avoid redundancy.

  3. Form Handling & Flattening

    Many Pokémon have alternate forms — some purely cosmetic, others with unique stats or evolutions. The script includes logic to:

    • Filter out cosmetic-only variants (like Spiky-Eared Pichu or Partner Pikachu)
    • Include mechanically distinct forms (like Galarian vs. Hoenn Zigzagoon) when they evolve differently

    This ensures that only forms with mechanical impact appear in the output — keeping the list both useful and uncluttered.

  4. Family-Based Sorting Strategy

    Rather than listing Pokémon in strict National Pokédex order (which splits families across generations), the script sorts by evolutionary family first:

    1. Determine the lowest National Dex number within each family
    2. Use that number as the family's sorting key

    This strategy ensures that all related Pokémon — regardless of when they were introduced — appear together. It's especially helpful when organizing Pokémon Home boxes where cohesion matters more than release order.

Edge Case Handling

Handling edge cases is critical when working with a dataset as large and complex as the one provided by PokéAPI. I wanted to address these cases elegantly in order to produce results that I'd be proud to use in my own Pokémon organization.

The evolution system in the Pokémon universe has grown extensively over the years and is now far more complex than it was when Pokémon Red, Green, and Blue first launched. Here are some of the trickier edge cases I had to account for when writing the script:

  • Pokémon Species with Multiple Forms

    Some Pokémon species have alternate forms that are treated as separate entries in Pokémon Home. These needed to be grouped thoughtfully to reflect their shared identity while still accounting for their differences. A few examples include:

    • Voltorb (Kantonian and Hisuian forms)
    • Raichu (Kantonian and Alolan forms)
    • Deoxys (Normal, Attack, Defense, and Speed forms)
    • Basculin (Red, Blue, and White Stripe forms)
  • Branching Evolutions

    Some species, like Eevee, can evolve into multiple different Pokémon based on conditions such as time of day, friendship level, held items, or proximity to specific in-game locations. To support these, the script recursively walks the entire evolution chain, collecting all possible branches. This ensures that all members of a family — no matter how complex — appear together in the final output.

  • Regional Forms with Diverging Evolutions

    Certain Pokémon have region-specific forms that evolve into completely different species. One clear example is Yamask:

    • Unovan Yamask evolves into Cofagrigus
    • Galarian Yamask evolves into Runerigus in Pokémon Sword and Shield

    Because PokéAPI treats these as the same base species, the evolution chain data doesn't always make this distinction clear. My script compensates by including both regional branches in the same family group, giving a more complete and accurate representation of that species' evolutionary family.

What I Learned

My goal in building this script was not only to create something personally useful, but also to grow as a developer in the process. I'd worked with many of the key concepts featured in this project before, but developing PokeFamilyDex gave me the opportunity to explore them more deeply. Here are some of the major skills and technologies I was able to strengthen along the way:

  • API Integration & Data Normalization

    While I've previously worked with RESTful APIs (including designing and using the backend for Flare), I hadn't had as much experience working with third-party APIs that I didn't help build. One of my goals with this project was to practice integrating with an external API — one that I had no control over.

    This meant I had to work with the PokéAPI as-is and adapt my code to its structure, limitations, and quirks. Parsing the JSON data returned by the API presented challenges around data consistency and normalization. To handle this, I built my logic to be flexible and robust — especially in the face of unusual edge cases, which I've discussed further in the Edge Case Handling section.

  • Caching Strategies

    Optimizing the script to avoid excessive API requests was a high priority. To achieve this, I implemented JSON caching early in development. By storing species and evolution chain data locally, I was able to dramatically reduce load times and ensure the script could be run repeatedly without putting unnecessary strain on the PokéAPI.

    Before this project, I'd had little hands-on experience with caching. I was pleasantly surprised by how simple and effective it was to integrate into a local scripting context.

  • Clean Code Practices

    Writing clean, maintainable code is something I always strive for. I've worked on too many personal projects in the past that became hard to read or extend because I rushed through early stages without proper structure. This time, I was intentional about keeping the script well-organized from the start.

    I focused on breaking the logic into clear, purpose-driven functions, minimizing global state, and following consistent naming patterns. The result is a script that's easy to navigate and modify — something I'll be able to extend confidently in the future.

Upcoming Features

My MVP (Minimum Viable Product) for PokeFamilyDex was intentionally limited. I knew I'd spend a significant amount of time early on learning how to retrieve, clean, and cache the data. My goal for the initial version was to ensure the script could do the following:

  1. Retrieve data from the PokéAPI for all Pokémon forms that can be stored in Pokémon Home, including:

    • All base Pokémon forms
    • All form differences (such as Deoxys and Deerling)
    • All regional variants of existing Pokémon (such as Alolan Raichu and Hisuian Basculin)
  2. Sort and organize the retrieved data
  3. Cache data to speed up future runs and reduce unnecessary API calls
  4. Flatten evolution chains into linear family lists
  5. Organize Pokémon using the following priority:
    1. Pokémon Family
    2. National Pokédex Numbering
  6. Output the data to a readable text file

Now that those core features are working, I can shift my focus to “nice-to-have” features that will make the tool more customizable and collector-friendly. Here are some ideas I'd like to implement in the future:

  • Format the output file to include breaks between individual Pokémon boxes
  • Include Gigantamax forms in the output
  • Add user-configurable options to include or exclude certain categories, such as:
    • Gender differences (e.g., Frillish, Indeedee, Basculegion)
    • Regional variants (e.g., Galarian Zigzagoon, Paldean Wooper)
    • Gigantamax forms
    • Alolan Totem Pokémon
    • Legendary Pokémon
    • Mythical Pokémon
  • Allow users to export a visual representation of their organized Pokémon boxes

This is an ongoing passion project of mine, so stay tuned for updates!