Skip to content

$ cat ~/blog/coding-for-the-cats.md

 · Updated  · Long read  · 7 min read ·  #wordpress #open-source #side-project #cats

Coding for the cats

I built a free WordPress plugin that places CJ Paws' public Petfinder listings in a filterable, on-brand grid without a Petfinder API key.

Story overview 6 main sections
  1. Update: the August 29 source snapshot
  2. The problem nobody asks you to solve
  3. The trick, for the folks who want it
  4. Why CJ Paws gets the Saturday
  5. Coding for the cats

Visual story map

How this long read moves

  1. Section 1 of 6: Update: the August 29 source snapshot
  2. Section 2 of 6: The problem nobody asks you to solve
  3. Section 4 of 6: The trick, for the folks who want it
  4. Section 5 of 6: Why CJ Paws gets the Saturday
  5. Section 6 of 6: Coding for the cats
Follow the main sections, or use the outline below to jump to a specific question.

Most of what I build is for the people who produce the legal record. This one’s for cats.

It’s a little WordPress plugin called Purrfect Match, and it does one thing: it puts a rescue’s adoptable pets on their own website, in their own look — a clean, filterable grid instead of a gray box that belongs to someone else. I built it for CJ Paws, the cat rescue here in St. Pete. That’s Sushi up in the header, supervising. She supervised the build too, which mostly meant sitting on the keyboard.

Update: the August 29 source snapshot

Source reviewed August 29, 2026.

The note below documents the first version I built in June. The August 29 source snapshot reports version 1.7.1. I reviewed that state at commit 34138c5. That review found no tagged GitHub release, so 1.7.1 here is the version in source, not a release claim or a claim about today’s main.

The front end has grown up since the first pass:

  • The 1.7.1 pass keeps host themes from replacing the widget’s typography or adoption-button label colors, balances filters at medium embed widths, and removes the organization link when it would only point back to the same site.
  • The listing view now uses a dedicated filter deck, removable filter chips, Shuffle, 4:3 photos that shift to 3:2 on narrow containers, aligned actions, and an explicit Load more path.
  • A pet story can open over its card when Petfinder provides a description. Keyboard focus moves into the story, Escape closes it, and focus returns to the control that opened it.
  • A rescue can optionally connect an adoption form. The link carries the pet’s name and ID so the form can identify the intended animal.
  • Settings cover one or more organization IDs, animal type, listing status, batch size, two to four columns, copy, color, card details, fallback links, and a live style preview. There is also a Petfinder Explorer for checking the configured source.
  • That source can emit AnimalShelter and pet ItemList JSON-LD. Its public controls are labelled, result updates use a concise live region, and motion respects the visitor’s reduced-motion preference.

The data boundary changed too. In that snapshot, the browser keeps a short local copy and can request Petfinder’s public widget data directly. The source also includes an optional shared WordPress cache enabled at a 120-minute default. Visitors may read that shared copy; only capable logged-in users can refresh it. The plugin includes no analytics and stores no Petfinder API key, but Petfinder and its photo CDN still receive ordinary connection metadata when they serve data or images.

The original note continues below with September 4 clarifications about the external integration. When it says there is no server-side cache or that the shortcode is the entire setup, it is describing the first version, not the August snapshot or today’s main.

The problem nobody asks you to solve

The goal was to put CJ Paws’ adoptable cats on its own site: its page, its colors, and the cats front and center. The June note pointed readers toward Petfinder API-key registration as the official integration path.

Clarification — September 4, 2026: The Petfinder developer URL now redirects to the general homepage, so this post no longer recommends that registration path. That observation does not establish when API access changed. The implementation described here depends on an external widget endpoint; public visibility alone is not a stable integration contract or a grant of reuse permission.

Here’s the thing about API keys in 2026. An API is a promise, and the last few years have been a slow lesson in how easily promises get revoked. Reddit, Twitter, a dozen quieter ones — doors that were open to anyone who could write a little code, now locked, metered, or gone. Build your shelter’s adoption page on top of someone’s API and you’re one pricing email away from a blank page. For a volunteer-run rescue, “the cats stopped showing up and nobody knows why” is not a great Tuesday.

So I didn’t ask for a key.

What it actually does

If you’re not technical, here’s the whole story: Petfinder already publishes a small embeddable widget — the little scroller of a shelter’s pets you’ve seen on rescue sites. That data is already public. My plugin asks for that same public data, then draws it the way CJ Paws would want it drawn — their pink, a real grid, instant filters for breed, size, and age. Drop one line on a page and you’re done:

[purrfect_match organization="FL1629" brand="#e93396"]

FL1629 is CJ Paws’ Petfinder ID. #e93396 is their pink. That’s the entire setup.

Open the CJ Paws adoption grid → to see the listing on this site.

The trick, for the folks who want it

For the engineers: the first version reproduced Petfinder’s public pet-scroller data layer client-side. When a visitor opened the page, their browser — not my server — called the widget endpoint: a GetOrganization query resolved the display ID to an org UUID, then SearchAnimal returned the adoptable animals. That describes the implementation, not a guarantee that browser requests will always be accepted. The original claim that they would never trip bot protection was too broad: challenge systems can interrupt legitimate browsers too. Request headers and browser execution are not permission to bypass a provider’s restrictions.

That first version needed no API key, scheduled sync, or server-side cache. It still depended on Petfinder’s public widget endpoint; a change or interruption there could stop the grid. Keeping a direct link to the rescue’s adoption page matters for exactly that reason.

I kept it honest about its limits, too. The filters run in the browser on whatever the public query returns — breed, size, age. Gender is a valid Petfinder filter input, but it isn’t returned per animal, so I left the gender filter out rather than ship one that quietly lies. Same rule I hold AI to: if it isn’t in the data, don’t pretend. Apparently I apply it to cats now.

Why CJ Paws gets the Saturday

CJ Paws is a volunteer-driven, donation-supported, 501(c)(3) cat rescue in St. Petersburg. It’s foster-based — the cats live in real homes, not cages — and it got started because of one resilient kitten named Zoe. They describe themselves as running on “purrs, passion, and a whole lot of love,” and after watching them work, that’s not marketing.

What I like most is that the adoption process is unapologetically human. You apply, you meet the cat in its foster’s living room, someone does a quick home visit, and then — this is the part that tells you everything — they stay in touch. These are people who want the match to actually stick. Plugin’s named after the good version of that.

Coding for the cats

This isn’t a product. There’s no roadmap, no pricing, no growth plan. It’s GPL and free, it ships with generic defaults so any rescue can drop in their own Petfinder ID, and the code is on GitHub. The entire reward is a cat getting seen by someone who’s idly scrolling — and then not being on the page next week, because they got adopted.

I open-source the unglamorous plumbing in my legal-tech work for the same reason I spent a Saturday on this: some things just shouldn’t require anyone’s permission. A 45-year-old firm shouldn’t have to beg a vendor to open its own recordings, and a cat rescue shouldn’t have to beg an API for the right to show its own cats. Obiwan would’ve supervised this one from the windowsill — the way he still does, in memory.

If you’re in Tampa Bay with a lap going spare, the cats are right here: cjpaws.org/adopt. If you run a rescue and want the plugin, it’s yours.

— Director of Technology at Executive Reporting Service and the builder behind DepoStack, based in St. Pete. Read The Log