Booking links — find a person's Calendly or Cal.com link and read its form
Finds the public booking link (Calendly, Cal.com, SavvyCal, HubSpot…) a named person publishes — on their company's pages, their GitHub, an old archived…
Finds the public booking link (Calendly, Cal.com, SavvyCal, HubSpot…) a named person publishes — on their company's pages, their GitHub, an old archived copy of their site, or under their own name on Calendly and Cal.com — and reads what the booking form asks and whether the calendar is open, without booking anything.
Also known as: booking link, booking links, calendly link, cal.com link, find calendly, find someone's calendly, scheduling link, book a meeting with, meeting link, public calendar, savvycal, booking form questions
Call it
bowmark.booking_links.find(person: FindBookingLinksInput, options?: FindBookingLinksOptions): Promise<BookingLinkSearch>
bowmark.booking_links.scanPage(url: string, options?: CallOptions): Promise<PageScan>
bowmark.booking_links.read(url: string, options?: ReadBookingPageOptions): Promise<BookingPage>Functions
| Function | What it does |
|---|---|
find | Finds a person's public booking links from their name. |
scanPage | Lists every booking link written on one page — hrefs, Cal.com embed buttons (data-cal-link) and plain-text links — with each link's anchor text and the words around it, ignoring the… |
read | Reads a Calendly or Cal.com booking page without booking: who owns it, the events it offers, every question its form asks, qualifyingQuestions (anything beyond name/email/notes — a sales… |
Types
type BookingPlatform = "calendly" | "cal.com" | "savvycal" | "tidycal" | "zcal" | "hubspot"
| "acuity" | "chilipiper" | "google-calendar" | "microsoft-bookings"
// published: READ off a page (foundOn is the citation). archived: read off an old
// Wayback capture of a page (archivedAt dates it). indexed: Google's site:cal.com search for
// the name returned it (context quotes the result). name_match: built from the name and
// it exists — nothing shows the person published it, and a namesake can own it.
type BookingLinkMethod = "published" | "archived" | "indexed" | "name_match"
interface BookingLinkFinding {
url: string
platform: BookingPlatform
method: BookingLinkMethod // the strongest way it was found
foundBy: BookingLinkMethod[] // every way it was found
foundOn: string | null // the page it was written on
archivedAt: string | null // YYYY-MM-DD capture date, for "archived"
archiveUrl: string | null
context: string | null // the link's anchor text or the words around it
ownerName: string | null // the name the booking page itself shows (name_match)
nameConfirmed: boolean // name_match: page owner carries both names; published/archived: the name is on that page
}
interface FindBookingLinksInput {
name: string // full name, first and last
company?: string // name or domain — adds "first-company" slugs
domain?: string // their company site: about/team/contact/services pages + homepage scanned
github?: string // a handle READ off a page that names them, never guessed
urls?: string[] // personal site, speaker bio, blog post — any page of theirs
}
interface FindBookingLinksOptions {
archive?: boolean // read Wayback captures of the domain's about/team/contact pages; default true with a domain
search?: boolean // Google site:cal.com "<name>" via Serper; default true; each search is charged to your account, or send your own key as the x-bowmark-vendor-key-serper header
timeoutMs?: number
}
interface BookingLinkSearch {
name: string
links: BookingLinkFinding[] // published first, then archived, then name_match
checked: { pagesScanned: number; pagesUnreachable: string[]; archivedPagesRead: number; slugsChecked: string[] }
warnings: string[]
}
interface PublishedBookingLink { url: string; platform: BookingPlatform; context: string; nearbyText: string }
interface PageScan { url: string; finalUrl: string; status: number; links: PublishedBookingLink[]; warnings: string[] }
interface BookingQuestion { label: string; kind: string; required: boolean; choices: string[] }
interface BookingEvent { name: string; url: string; description: string | null; durationMinutes: number | null }
interface BookingPage {
url: string
platform: "calendly" | "cal.com"
ownerName: string | null
organization: string | null // the org/team the platform files the account under (Cal.com)
avatarUrl: string | null
events: BookingEvent[] // every event a profile lists
event: BookingEvent | null // the event whose form was read; null if several and none named
questions: BookingQuestion[] // everything the form asks
qualifyingQuestions: string[] // the questions beyond name/email/notes — "company", "team size": a sales funnel asks these, a personal chat does not
bookable: "open" | "fully_booked" | "closed" | "unknown" // Cal.com is always "unknown" here; use cal_com.getAvailability
nextAvailability: string | null
unavailableReason: string | null // the platform's own words when it cannot be booked
warnings: string[]
}
interface ReadBookingPageOptions { event?: string; timeoutMs?: number }
type CallOptions = {
timeoutMs?: number // per-provider budget in ms, default 30000, clamped to 1000-55000.
// A provider slower than this is DROPPED from the results and
// NAMED in warnings — never silently absent
}Examples
// Find a founder's booking links, strongest evidence first.
const { links, warnings } = await bowmark.booking_links.find({
name: "Caleb Peffer", company: "firecrawl", domain: "firecrawl.dev",
});
for (const l of links) log(l.method, l.url, l.foundOn ?? l.ownerName);
return { links, warnings };// What will this booking page ask, and is it a sales funnel?
const page = await bowmark.booking_links.read("https://cua.cal.com/f/cua-demo");
return { owner: page.ownerName, org: page.organization, asks: page.qualifyingQuestions };Providers behind it
| Provider | |
|---|---|
calendly | Calendly |
cal_com | Cal.com |
archive_org | Wayback Machine (archive.org) |
github | GitHub |
serper | Serper |