wrote a cli property deal scorer in go and goroutines made the batch lookups absurdly fast
a friend invests in rental properties and he kept sending me lists of addresses asking me to look them up on zillow. like 15-20 addresses at a time, usually from a mailer list he gets from his county. i'd spend 20 minutes tabbing through zillow pages and texting him back which ones were worth looking at.
wrote a cli tool in go that takes a text file of addresses, looks them all up in parallel, scores each one, and spits out a sorted list. the whole thing is a single binary he runs on his laptop.
the property data comes from a rest api called zillapi. you give it an address, it returns zillow data as json. zestimate, asking price, rent estimate, tax records, price history. 300+ fields per property. i only use about 10 for the scoring but the full response is there if i need more later.
the interesting part is the concurrency. the api takes about 1-2 seconds per call. doing 20 addresses sequentially would take 30+ seconds. with goroutines and a semaphore it takes about 3 seconds total:
go
func scoreAll(addresses []string) []Result {
sem := make(chan struct{}, 10)
results := make(chan Result, len(addresses))
var wg sync.WaitGroup
for _, addr := range addresses {
wg.Add(1)
go func(a string) {
defer wg.Done()
sem <- struct{}{}
defer func() { <-sem }()
data, err := lookupProperty(a)
if err != nil {
results <- Result{Address: a, Error: err}
return
}
results <- Result{
Address: a,
Zestimate: data.Zestimate,
AskPrice: data.Price,
RentEst: data.RentZestimate,
Score: scoreDeal(data),
}
}(addr)
}
go func() {
wg.Wait()
close(results)
}()
var out []Result
for r := range results {
out = append(out, r)
}
sort.Slice(out, func(i, j int) bool {
return out[i].Score > out[j].Score
})
return out
}
semaphore of 10 so i'm not hammering the api with all 20 at once. the scoring is simple. rent-to-price ratio above 0.8% and asking price below zestimate is green. one condition met is yellow. neither is red. the output is sorted best to worst so the green deals are at the top.
the cli usage looks like:
$ propertyscore -file addresses.txt
GREEN 1425 Oak St, Springfield ask: $285k zest: $312k rent: $2,100
GREEN 892 Maple Ave, Springfield ask: $195k zest: $218k rent: $1,650
YELLOW 3301 Pine Rd, Springfield ask: $340k zest: $335k rent: $2,800
RED 710 Elm Ct, Springfield ask: $420k zest: $389k rent: $2,200
my friend runs this every tuesday when his county mailer list comes in. takes 3 seconds for 20 addresses and he immediately knows which ones are worth driving by. before this he was spending an hour on zillow every tuesday morning.
for the ai side i also set up a skill so he can ask claude about properties:
npx clawhub@latest install zillow-full
the binary is 8mb. no dependencies beyond the stdlib and the standard json/http packages. i compiled it for his windows laptop with GOOS=windows and just texted him the exe. no docker, no install process, no runtime. that's the part of go i appreciate most for tools like this.