In the last blog post, we discussed the basic principle behind how we can use the UPI system to build a somewhat accurate but limited caller ID system that can be more privacy friendly as compared to mass data collecting organizations like Truecaller, the advantages and disadvantages of such a system, and the release of a small proof of concept tool written in Bash to help demonstrate the technique.
Today, I detail how I used such a system to solve a somewhat non-trivial problem: saving everyone's phone numbers.
I am in a whole bunch of college groups, and sometimes people contact me, but I do not have their numbers saved. I am also a very lazy person, so I forget to save people's numbers as well. This has been an annoying problem for me, and rather than just go through my call logs and save the numbers of people I am most frequently in contact with, I decided to spend some time building a tool that would just save the numbers of mostly everyone I could possibly be in contact with.
Hey, it was worth it according to xkcd!
I had opened up WhatsApp Web and used the "Inspect Element" feature to grab a list of all the phone numbers in a group. This will likely change as WhatsApp does not like unofficial, third party code interfacing with its systems, so I did not automate this.
All these numbers were saved to a good old text file, from where
I ran it through the Unix sort utility to get rid of
duplicated numbers. After all this, I had ~500 phone numbers to
search for.
After this, it was time to build a small CLI that would iterate through every number and search for it. If it found a match, it would store it in a map, and after all searches are complete, would write it to a vCard file (.vcf) which would be imported into a phone's contact app, thereby getting access to most of the phone numbers.
I chose Go to write the CLI in as it was an easy, fast language with good concurrency support. I also wanted to practice writing more Go code and to make use of goroutines.
However, after writing the code to make concurrent requests, I would get ratelimited after just 2 or 3 requests. This was surprising since I had made many, many more requests using the proof of concept and never faced the issue.
I removed the concurrency code entirely and just made requests sequentially (with a 500ms waiting period to boot) and the problem went away. I suspect I was making requests way too fast.
Another problem I faced was that map writes can't really be concurrent, so I had to create a channel and then push results there. This wasn't ideal because I couldn't easily check if a match had already been found for a phone number. Hence I made all the code sequential. Perhaps if I utilized one of the many libraries out there for ratelimiting network requests, I could speed up the program, but this can be a topic for future research.
After letting the program run its course, I ended up with a VCF
file that wasn't entirely up to spec, so had to
tweak the VCF generating function
(writeResultsToVCF()) a bit. Luckily I had been
printing each result to standard output when I was building the
tool and redirecting it to a text file just in case something went
wrong, so I wrote a function to basically recreate the map from
this file (getBulkLookupResults()) and pass it to the
VCF writing function as if nothing had gone wrong.
I now have ~70% of the phone numbers associated with a name. This is just from two VPAs (PayTM and PhonePe). The success rate from such a small sample space is quite encouraging, and I have avoided adding more VPA suffices to search from because of this.
And all this just to avoid saving some phone numbers"¦..
That's all for today! Bye for now!
This website was made using Markdown, Pandoc, and a custom program to automatically add headers and footers (including this one) to any document that's published here.
Copyright © 2026 Saksham Mittal. All rights reserved. Unless otherwise stated, all content on this website is licensed under the CC BY-SA 4.0 International License