Anurag's Blog

LinkLog Home

Amateur Reverse Engineering

Or why you should have rate limiting in your menu!

Last month, in an unfortunate series of events, I decided to update my favorite calorie tracking app, after a year.

What followed was a week of chaotic tech escapades.

Fear the AI-fication

After updating the app, much to my disappointment, the app was AI-fied and bent out of shape. The one-click calorie tracking feature now took me 3 clicks. And cherry on the top, the offline calorie tracker feature was removed.

While there are lots of open-source calorie tracking apps available on F-droid, none of them have a reliable database for Indian foods.

So I decided to take matters into my own hands and what followed next was inevitable!

Data is the new oil

I decided to get/scrape/dig up all the calorie data from the app itself, and make an app out of calorie tracking. And since it was proprietary data, I knew it wasn’t going to be easy.

Still I dared to hope and tried the easier way first.

The easy way: In older versions, this app had an offline database and the data was downloaded on first setup. So I suspected that there must be a SQLite file somewhere on my phone responsible for it.

Since Android doesn’t show the system app data without root. Quickly, I set up an Android emulator, rooted it, installed a Root File explorer along with Shizuku, and started looking for calorie db in Android system files. It took quite a few retries, and at the end, I found a likely culprit, an 8MB SQLite file. Tried to open it in terminal via SQLite tool. And Bam! It couldn’t be read. Well, I wasn't inclined to spend more time decrypting it. I dropped the idea and moved to the hard way.

The hard way: In that emulator, I installed HTTP Toolkit. (which installs a custom certificate and allows you to inspect traffic from URLs, basically Wireshark for Android). After inspecting traffic for a while, I identified the culprit API responsible for fetching food data. It looked something like this:

[https://api.xyz.com/api/v3/food/details?food_id=<food_number>](https://api.xyz.com/api/v3/food/details?food_id=<food_number>)  

This returned Calorie, micro and macro nutrients data of that particular food_id as below:

{  
  "food": {  
    "food_id": 16349,  
    "food_name": "Cinnamon Vinegar Lemon Honey Water",  
    "food_rrr_score": 0.46897858934694503,  
  },  
  "food_measures": [
    {  
      "measure_name": "cup",   
      "calorie": 36.07447,  
      "carbs": 8.97298, "fats": 0.01917, "fibre": 0.36849, "proteins": 0.07263  
      "common_measure_name": "cup", "default_quantity": 1,  "food_id": 16349,  "food_type": null,  "id": 56099,  "measure_id": 56099,   "measure_volume": 250,  "measure_weight": 250,  "metric_unit": "grams",

      "micronutrient_details": {  
        "calcium": 8.02344,  "cholestrol": 0,  "iron": 0.08433,  "magnesium": 3.03118,  "saturated_fats": 0.00194,  "sodium": 9.53419,  "total_sugars": 8.22535,  "vitamin_a": 0.29053,  "vitamin_c": 1.87391,  "zinc": 0.04968  
      },  
    },  
      
    {  
      "measure_name": "ml",  
      "calorie": 0.1443,  
      .......  
      "proteins": 0.00859  
    }  
  ]  
}  

In the app, the micronutrient data was a paywalled feature, but via API it could be accessed for free. Was I crossing into piracy land already?

After that, I fiddled around with query parameters but couldn’t find a way to fetch data in bulk. The range of food was from 1 to around 2,50,000. The only option was to scrape the data, one query at a time. Sigh! This was gonna take weeks.

Rate limiting has always been the nemesis of scrapers.

And, a health company with 1 Crore+ downloads would definitely have implemented a server-side rate limiting, right?

Nope.

Rate Limiting 101

They didn't had any rate-limiting at all. Data Democracy for all.

It took me two days, a Python script and an infinite amount of patience to scrape all the data.

It didn’t made me feel smug though, since the first thing I drew in HLD interviews is a rate limiter. And even after being such a popular app, they didn’t implement it. Must be running on those free 1Million AWS credits, like one of my friend’s startups.

Oil is here, now what?

After that, I converted all that json data into a nice 250MB SQLite db. Data was split into three tables- for faster searching and indexing.

Now, I decided to make an Android app for Calorie tracking using that SQLite data. I was a little familiar with Flutter, so I decided to revise the basics first.

I researched for the best resources. Downloaded tutorials. Set up my local environment. I was stoked and on a roll. I finished the official flutter tutorial, made that minimal app. I started coding the calorie app all by myself.

And...

Vibe... What?

The difference between the UI I wanted and the UI I could make was too much.

As much as I didn't want to vibe code this project (After reading dozens of anti-AI and skill-atrophy articles), I gave in. I wrote a markdown document with all the specifications, data format, APIs, and gave it to Claude. Compiled the output, gave it to Claude again… You know the drill.

Wait, after I ran out of their free tier limits, I used Gemini. Thanks Google.

I had to make some code modifications, but at the end, the app was ready and it looked glorious.

Screenshot of App

Wait, it’s Calorie Searching, not Calorie Tracking!

Hard times create strong apps , good times create weak apps !

And now that I was feeling euphoric after building out the initial version, as is the rule of universe, progress was going to stall. The motivation was going to dry up.

So I did the next best possible thing, dropped the app-calorie-tracking-idea altogether, started vibe-tracking calories in my head and waited for the motivation to strike.
The motivation, like a freelancer’s income, arrived after a few days. And when I thought of acting on it, it was too late.
It so happened that I had to format my laptop in between. The most cherished local flutter development setup was gone. While I still had the code, setting things up, and fiddling again with the project, seemed like a daunting task.
In addition to that I figured that thinking of designing a lovely app is much easier than making it, even though the first one gave more dopamine hits.
So, just like my friend’s cat, I moved on to the next shiny thing, thinking of what ground-breaking stuff to make.