The term “hacking” conjures many images and thoughts in the mind. Dark rooms, black hoodies, electronic music. For me, hacking just means understanding the underlying systems and getting it to do what you want (perhaps in an unorthodox manner).
In that sense, the Fonz is a hacker

Like most people who had planned to get married in 2020, my plan needed to change. My partner and I wanted to stay safe during the pandemic — Thankfully, the great city of New York offered a way to remotely apply for a marriage license — Hooray!

Alas, not everything is so easy. Sure, make an account, get verified, pay a license fee — all good. However, we hit a snag upon actually scheduling a meeting with the City Clerk. It seems that it was as hard to snag an appointment with them as it is to get concert tickets.

So after a few days of logging in, navigating through the web app and checking the calendar for every available date in the next few months, I got tired of it and said “let’s automate this.” Well, in order to automate, we need to understand what’s happening under the hood.
Thankfully, it has never been easier for anyone to peek into what your browser is doing on the internet. Popping open the developer tools, you can take a look at each HTTP request your browser makes.
Whenever your visit a website, your browser makes HTTP requests for information. For example, typing in https://blog.mattmintzer.dev into your URL bar tells your browser to make an HTTP GET request for the resource at blog.mattmintzer.dev. That resource has further instructions and information for your browser to render my blog.

Pouring over the tremendous amounts of requests the browser made (modern web apps sure make a lot of requests), I found a URL (circled in red) that looked particularly relevant.
Let’s dissect this URL a bit. Let’s look at the query parameters (data in the form of key, value pairs that gets sent directly through the URL). They come after the question mark. (everything after /range?) Looks like the calendar range info gets set in these parameters (range_start and range_end). I played around and wanted to see if I could ask for data from the current date to the end of the calendar year so I modified the range parameter to the end of the year (2020-12-31) and just put that URL in my browser and see what happens.

Awesome! JSON Data! I also wanted to check that I could still get this data without being authenticated in the app so I just threw open an incognito tab, made the same request and confirmed we still got data, score!
JSON data is another kind of key-value pair data structure. Quick look at it makes it kind of clear. The response has a key of “days” which has a value of a list of what I’ll just call “day objects.” Each of these day objects has 4 keys. The one that piques my interest is the “status” key.
My brain is turning rapidly as I think of how to automate my problem away. Let’s just write a script that will send a request to the calendly link. Grab that “days” list, loop through the days searching for a day that has a status that is not “unavailable.” Once we have a match, I want to be notified so how about it sends me an email.
#!/usr/bin/env python3
import json
import requests
import smtplib
Libraries are how you solve your problems with someone else’s solutions
We need a few tools to make this hack work:
- python – Easy to read and write and useful libraries
- json – python library for parsing json data
- requests – python library for making HTTP requests
- smtplib – python library for sending email.
Next up, we need to actually check that calendar:
def check_calendar():
url = 'https://calendly.com/api/booking/event_types/BFEN3HYGXBSSUYOC/calendar/range?timezone=America%2FNew_York&diagnostics=false&range_start=2020-09-09&range_end=2020-12-29&single_use_link_uuid=&embed_domain=projectcupid.cityofnewyork.us&embed_type=Inline'
r = requests.get(url)
json_data = json.loads(r.text)
days = json_data['days']
found_date = False
for date in days:
if date['status'] != 'unavailable':
found_date = True
send_email(date['date'], date['spots'])
break
if not found_date:
send_email('nothing available', 'checkback tomorrow')
- I set the url I found from the web app as a string variable
- Use requests to send an HTTP GET request to the resource, and save the response to a variable called r
- I use the json library to parse the text into a python dictionary
- I’m specifically interested in the “days” key so I loop through each day checking for the availability
- If I find availability, I send myself an email with the dates
- If I don’t find one after checking every date, I’ll send myself an email to confirm my script is working and nothing is available.
Here’s the send_email function I wrote. Nothing too special.
def send_email(date, spots):
sender = '' # ADD GMAIL ACCOUNT HERE
sender_password = '' # ADD GMAIL PASSWORD HERE
to = ''#ADD EMAIL HERE
body = f'get married on {date} {spots}'
try:
server = smtplib.SMTP_SSL('smtp.gmail.com', 465)
server.ehlo()
server.login(sender, sender_password)
server.sendmail(sender, to, body)
except:
print('something went wrong')
The smtplib library allows me to login to a gmail account and send email. For ease of use, I set up a gmail account specifically for development. This is because you have to adjust gmail account’s security settings to allow access to the python code. (nevermind the fact that I didn’t want to spam actual email with test emails as I make sure this works). I’m using smtplib’s SMTP_SSL module to to ensure I use SSL for encrypted communication.
Once I was confident the script would work as expected, the next question was upon me. How/when would I use it? Sure, all I have to do now is make it executable and run it, but that means I still have to manually do it (upwards of a few keystrokes). We can do better. I’ve got a raspberry pi I use only as a pihole. Simple enough to get the script on the pi and set up a cronjob to run every morning at 9:45am and 11:45am. My reasoning is that I wasn’t sure when the calendar gets updated but assumed it would happen some time in the morning and I don’t mind receiving two emails a day


And now… we wait. And wait I did. It actually took 3 weeks of emails before I got a hit. Once I received the confirmation email, I scheduled the date.
It has been a few weeks since and we have been able to get married. Knowledge of some basic scripting and web helped me get married in the middle of a pandemic =)
Want to take apart the code I used yourself? Here’s a link to my github repo
