10 min read
A thousand pages, none of them fake
Ninety days. 1,693 organic clicks. More than a thousand pages. No single page broke 13 clicks in the window. The median was 1.
Those numbers belong to Portal Patas, a nonprofit animal adoption portal in Brazil. What I want to document here is not the mechanics of how it was built, but what the data taught me about local intent, about using real inventory as structure, and about a wrong assumption I carried into the project that the numbers eventually forced me to correct.
Context and the problem
Portal Patas connects independent animal rescuers, shelters, and families who need to rehome a pet with people who want to adopt. No commercial angle. The mission is reach: get adoptable animals in front of the people most likely to take them home.
The natural acquisition channel for this kind of connection is local search. Someone who wants to adopt a dog almost always wants a dog that is nearby. Not because cross-city adoption is impossible, but because the whole experience, meeting the animal, working out logistics, completing the adoption, is fundamentally easier with short distances.
The challenge is that “nearby” is an extremely specific criterion in a country with over 5,500 municipalities. In each of them, in any given week, someone is typing “dogs for adoption in [city name]” or “cats for adoption in [city name].” These are low-volume queries individually. But aggregated across hundreds of cities, they represent a substantial pool of high-intent qualified traffic, and per-query competition tends to be low because most sites do not invest at that level of granularity.
When I started working on Portal Patas, the site had a structural gap. The animal inventory existed, with location data attached to every listing. But the site architecture did not translate location into URL. There were pages by species. There were filters by state. There was no indexable page for the query “dogs for adoption in Alta Floresta.” The data existed. The page did not. Google does not index database records. It indexes URLs.
My role
I was the information architect and SEO strategist on this project. I did not write editorial content. I did not create animal listings. My job was to define how the existing inventory of available animals would become a structure of indexable pages, how that structure would stay current as inventory changed, and what signals each page needed to emit so Google understood what it offered.
The hard part was not technical. It was conceptual: figuring out the right unit of indexation for a site that matches local intent to dynamic inventory.
That question matters because the answer determines not just how many URLs exist, but how they behave when inventory changes. A site with ten static pages and a site with a thousand dynamic pages are architecturally very different from the crawler’s perspective.
The decisions that mattered, and what it would have cost to choose differently
The central decision was this: location needed to be a structural dimension of the URL, not a frontend filter.
When you have localized inventory, there are two ways to expose it. The first is a single page with frontend filters: the user goes to /dogs-for-adoption/, selects a city, and the list updates via JavaScript. This is simpler to build and maintain. You have one URL instead of hundreds.
The problem is that this approach is invisible to Google. When someone searches “dogs for adoption in Governador Celso Ramos,” Google has no page to index for that query. No URL exists. No geographic relevance signal. No title, no metadata, no page for the person searching. The search query exists. The answer does not.
The second approach is to create an indexable page for each species-city combination. This multiplies URL count significantly. It requires the URL structure to reflect live inventory, because a page for “cats for adoption in São João Batista” should only exist while there are actually cats available for adoption in São João Batista.
I rejected a third option that comes up regularly in programmatic SEO discussions: building local pages with no real inventory behind them, just generic text, expecting to rank on structure alone. Some projects do this. You create the URL, add a paragraph about responsible adoption, drop in a list of animals unrelated to any specific city, and wait for traffic to arrive.
What actually happens in that model is that you earn impressions and fail on clicks. Google surfaces the page because the title matches the query. The user clicks and finds a list of animals from another region, or copy that does not answer their question. Bounce rate climbs. Session time drops. You are actively training Google to treat your domain as a poor match for local adoption intent.
For Portal Patas, this option was not just inefficient. It was wrong. The project cannot serve a page for “rabbits for adoption in Natal” that has no rabbits in Natal. That is not an SEO problem. It is a false promise to someone who is genuinely trying to find an animal to adopt.
Tying page existence to real inventory has an implementation cost, and that cost is ongoing. But an 8.4% CTR at average position 9.0 has no other explanation: it only happens when the user who arrives on the page finds exactly what they searched for. Every model that trades specificity for reach ends up training the search engine in the wrong direction.
What went wrong
I made a wrong assumption at the start of this project.
My implicit model was that search volume would follow city population. Major cities and metropolitan areas would generate more searches, so that is where I should focus inventory coverage monitoring and performance tracking. It seemed like a reasonable prior.
The Search Console data broke that assumption.
Of the ten pages with the most clicks in the last 90 days, most correspond to small or mid-size cities. Alta Floresta, in the state of Mato Grosso, has roughly 60,000 inhabitants and is one of the top-performing pages on the site, with 13 clicks in the window. Remanso, in Bahia, with 40,000 people, holds an average position of 4.5 for the dog adoption query and drove 9 clicks. Governador Celso Ramos, in Santa Catarina, has 15,000 people and generated 8 clicks.
The mistake was not technical. It was a wrong premise about how local traffic actually works.
The logic of local programmatic SEO is not volume per page. It is intent density multiplied by the absence of competing answers. In a major city, dozens of sites are already answering “dogs for adoption in São Paulo.” In Alta Floresta, Portal Patas may be the only relevant result. Lower competition, higher relevance, strong CTR even from mid-page positions.
If I had internalized this model from day one, I would have prioritized inventory coverage for small and mid-size cities from the start, rather than treating population count as a reliable proxy for search opportunity.
The lesson I carry is that “important cities” is a useful filter for where you invest editorial effort, not a filter for where search intent exists. Intent exists wherever people live, regardless of how many of them there are.
Results, with evidence
Measurement window: June 22 to September 19, 2026 (90 days).
Source: Google Search Console, service account with read access to property portalpatas.com.br.
Summary
| Metric | Value |
|---|---|
| Total clicks | 1,693 |
| Total impressions | 20,126 |
| Average CTR | 8.4% |
| Average position | 9.0 |
| Pages with impressions | more than one thousand |
Top 10 pages by clicks in the period
| Page | Clicks | Impressions | Avg position |
|---|---|---|---|
| /cachorros-para-adocao-em-alta-floresta/ | 13 | 120 | 9.6 |
| /coelhos-para-adocao-em-salvador/ | 13 | 58 | 7.2 |
| /gatos-para-adocao-em-alta-floresta/ | 10 | 49 | 8.6 |
| /gatos-para-adocao-em-araxa/ | 10 | 62 | 10.0 |
| /cachorros-para-adocao-em-remanso/ | 9 | 26 | 4.5 |
| /cachorros-para-adocao-em-governador-celso-ramos/ | 8 | 32 | 8.7 |
| /coelhos-para-adocao-em-teresina/ | 8 | 51 | 6.8 |
| /gatos-para-adocao-em-xanxere/ | 8 | 38 | 9.4 |
| /coelhos-para-adocao-em-vitoria-da-conquista/ | 7 | 128 | 9.1 |
| /gatos-para-adocao-em-matinhos/ | 7 | 44 | 9.6 |
The pattern the data shows is a textbook long tail: 733 of the pages with at least one click had exactly one click in the period. That is 73% of all pages with a click within the first thousand rows of the API. Only 37 pages had four or more clicks. Total traffic does not come from a cluster of strong pages. It comes from the aggregate of hundreds of hyper-specific queries, each with low individual volume and high intent.
The number that validates the model is CTR. At average position 9.0, you are roughly in tenth place. Converting 8.4% of impressions to clicks from that position means users are recognizing the page as the right answer to their specific query. That does not happen with placeholder content. It happens when the page delivers exactly what its title promised.
The stack, in broad terms
Portal Patas runs on WordPress, with pages generated from the inventory of animals available for adoption.
The advantage is not technological. It is editorial: the decision to never publish a page without real data behind it. That decision has an implementation cost. The return is a CTR that has no other explanation.
Click distribution per page
The chart below shows the distribution of clicks across pages that received at least one click in the period, within the thousand-row limit returned by the Search Console API. All thousand rows had at least one click, confirming that more than a thousand pages have impressions in total.
The curve starts at 2 pages at the peak and ends at 733 at the base, forming the characteristic long-tail shape of traffic distributed across hyper-specific local intent.
If you run a site with localized inventory and want to understand how to structure it for organic search, see the full project: Portal Patas in the portfolio.
Get the next case studies before everyone else: subscribe to the newsletter.
Newsletter
Uma decisão técnica por post. Nada mais.
Bastidores reais de quem constrói empresa operada por IA. Sem hype.
Dupla confirmação. Sem spam. Cancele quando quiser. Dados usados exclusivamente para esta newsletter (LGPD).