The Day Finding the API Wasn't the Solution
A deep dive into child APIs, hidden search architectures, dynamic validation, and browser-dependent workflows inside a modern Vue.js application.

When I first opened the Chinese portal, I expected a fairly standard workflow.
Most government portals follow a familiar pattern:
Search
↓
API Request
↓
Results
↓
Detail API
Find the search API.
Find the detail API.
Build the scraper.
Move on.
That assumption lasted about thirty minutes.
First Observation: The Site Felt Different
The first clue was the frontend itself.
The application was heavily dynamic.
Page transitions were fast.
Search results appeared without full page reloads.
Developer tools immediately showed evidence of a modern SPA architecture.
Initial inspection suggested:
Vue.js frontend
Dynamic rendering
Client-side routing
API-driven content loading
Nothing unusual so far.
Most modern applications look exactly like this.
The interesting part came when searches were executed.
Watching the Network Tab
I began by monitoring every request generated during a search.
My expectation was simple:
User Search
↓
Search API
↓
Result List
I expected to see a request containing:
keyword=aspirin
or
searchTerm=aspirin
or something similar.
Instead, the request chain looked far more fragmented.
Multiple requests appeared.
Several configuration endpoints loaded.
Metadata requests loaded.
Dictionary lookups loaded.
Reference data loaded.
Then finally some APIs started returning actual product information.
False Victory #1
At this point I thought I had already found the solution.
One endpoint returned:
Product Name
Registration Number
Manufacturer
Approval Information
Everything looked perfect.
I copied the request.
Replayed it.
It worked.
At least initially.
I remember thinking:
Nice. This project will be finished quickly.
Then I looked closer.
The endpoint required an identifier.
Something like:
id=123456789
The endpoint itself wasn't generating data.
It was only consuming identifiers.
The question became:
Where does the identifier come from?
Understanding Child APIs
The endpoint was actually a child API.
Its responsibility was simple:
Identifier
↓
Detailed Record
This explained why it looked so useful.
The portal already knew which record the user wanted.
The API simply returned details.
The problem was that I didn't know how to obtain millions of identifiers.
Without identifiers:
No Identifier
↓
No Child API
The investigation shifted completely.
The objective was no longer finding APIs.
The objective became finding the source of identifiers.
Hypothesis #1: Hidden Search Endpoint
My first assumption was straightforward.
Surely there must be a search API somewhere.
Something like:
keyword
↓
search endpoint
↓
identifier list
So I began filtering requests.
Search.
Keyword.
Page.
List.
Query.
Product.
Drug.
Registration.
Everything that looked remotely relevant.
Nothing obvious appeared.
That was unusual.
Almost every application exposes the search request somewhere.
This one didn't.
The Rabbit Hole Begins
The deeper I looked, the stranger the architecture became.
Several possibilities emerged.
Possibility 1
The search request was encrypted.
Possibility 2
The search request was generated dynamically.
Possibility 3
The search request occurred through WebSocket communication.
Possibility 4
The search request occurred through internal browser logic before being transformed.
Possibility 5
The search architecture was split across multiple requests.
At this stage I had more questions than answers.
Replay Testing
Whenever I find an endpoint, I always test whether it can be replayed independently.
The process is simple.
Capture request.
Copy request.
Replay request.
Observe behavior.
Several endpoints failed this test.
Instead of JSON responses I received:
Empty responses
Validation errors
Login pages
Generic error messages
The endpoint clearly existed.
The endpoint clearly worked inside the browser.
The endpoint clearly failed outside the browser.
That contradiction became the next clue.
Dynamic Values Everywhere
I started comparing successful requests.
Certain fields continuously changed.
Examples included:
timestamp
nonce
signature
token
Every request appeared slightly different.
Even identical searches generated different values.
This suggested runtime generation.
The browser wasn't simply sending requests.
The browser was constructing requests.
Huge difference.
Session Dependency
The next experiment involved sessions.
I tested:
Fresh browser
Existing browser
New cookies
Old cookies
Different accounts
Expired sessions
Results suggested a strong dependency on browser state.
A request copied from one session frequently failed in another.
This meant:
Request
Session
Runtime Context
were all contributing factors.
Vue Bundle Investigation
Once network analysis stopped producing answers, I moved to frontend analysis.
This was probably the most frustrating phase.
Production Vue applications typically ship as:
chunk-vendor.js
app.js
runtime.js
Thousands of lines.
Minified.
Bundled.
Obfuscated.
Reading them feels like trying to reconstruct a machine from melted metal.
Still, clues existed.
Search-related functions appeared.
Request construction logic appeared.
Token generation appeared.
Validation functions appeared.
But the overall workflow remained fragmented.
Each answer generated three new questions.
The Signature Problem
One particular area consumed a surprising amount of time.
Signatures.
Several requests appeared to include generated validation values.
The challenge wasn't finding the signature.
The challenge was determining:
What generated it?
When was it generated?
Which parameters contributed?
Was it deterministic?
Was it session-bound?
Every failed replay pointed back toward this question.
When Reverse Engineering Stops Being API Discovery
This was the moment I realized the project had changed.
Initially I was performing API discovery.
Now I was performing architecture discovery.
Those are completely different activities.
API discovery asks:
What endpoint exists?
Architecture discovery asks:
Why does this endpoint exist?
Who calls it?
What generates its parameters?
What systems support it?
Much harder problem.
The Turning Point
Eventually I stopped trying to reproduce requests.
Instead I started reproducing behavior.
Rather than:
Replay Request
the new strategy became:
Open Browser
↓
Perform Search
↓
Capture Identifiers
↓
Use Child APIs
The difference was subtle but enormous.
The browser already understood all hidden validation requirements.
Instead of fighting the application architecture, I began leveraging it.
That shift solved more problems than weeks of replay attempts.
Final Architecture Theory
After weeks of analysis, the architecture appeared closer to:
User Search
↓
Vue Components
↓
Frontend Validation
↓
Runtime Parameter Generation
↓
Internal Search Service
↓
Identifier Generation
↓
Search Results
↓
Child APIs
↓
Detailed Records
The child APIs were never the hard part.
The hidden identifier generation layer was.
What This Project Taught Me
The biggest lesson was surprisingly simple.
Finding an endpoint is easy.
Understanding a system is hard.
Modern applications increasingly hide critical business logic behind:
frontend frameworks
runtime validation
browser-generated parameters
session-aware security controls
The endpoint you see in DevTools is often only the final visible layer.
The real architecture frequently lives somewhere else.
And sometimes the most important API is the one you never manage to see.
