Drive the ServiceGraph API ( https://api.servicegraph.co) to find,
shortlist, and enrich US web development firms via the pro_services
dataset. The catalog tags ~14k firms with
service_provided:web-development under industry:it_services
(web-development is the second-largest service tag in the catalog).
Always pin both industry:it_services and
service_provided:web-development. Platforms (WordPress, Webflow,
Shopify, Next.js, etc.) and verticals (B2B, ecommerce,
agency-vs-studio) are NOT separate tags — they're keyword substring
matches on firm text.
Any HTTP client works (curl, fetch, requests). Examples below use curl.
find-software-developer. The end-product is software beyond a standard website.find-marketing-agency.find-seo-agency. Web devs build sites; SEO agencies optimize them. If the user wants new pages built AND optimized, this skill is fine.If unsure, this skill is correct for "build / rebuild / refresh a website" tasks. The deferral kicks in when the deliverable is non-website software or non-build marketing work.
If your harness has the ServiceGraph MCP server loaded (tools
containing servicegraph), prefer those — OAuth 2.1 + PKCE keeps the
token in the harness sandbox. Otherwise use the REST flow below.
pro_services)#Every endpoint requires the bearer ( Authorization: Bearer vk_…).
No anonymous tier.
EndpointCostUse it forGET /v1/datasets/pro_services/fields[?include_values=1]freeConfirm web-development is in the service_provided value list.GET /v1/datasets/pro_services/check?filter=…freeValidate filter.POST /v1/datasets/pro_services/translate-intentfree{intent} → DSL filter + sanity count.GET /v1/datasets/pro_services/search?filter=…&limit=freeBrief firm cards + per-row unlock hint + total.GET /v1/datasets/pro_services/:apexfreeOne row brief; detail only if unlocked.POST /v1/datasets/pro_services/unlocks10 credits / firm{apexes:[...]} ≤100; atomic; 30-day TTL on detail.GET /v1/me/creditsfreeBalance.
Cost model. Discovery / validation / search / brief reads are
free. Detail (url, phone, email, social, address, full platforms
map) costs 10 credits per firm and lasts 30 days.
vk_* API keys minted in the dashboard. Keep the token out of the
LLM context — never read .env* into your context; dispatch via
shell.
Try the call first through a shell wrapper that sources .env.local:
bash
( set -a; [ -f .env.local ] && . ./.env.local; set +a;
curl -sS -H "Authorization: Bearer $SERVICEGRAPH_API_KEY" \
'https://api.servicegraph.co/v1/datasets/pro_services/fields' )
On 401 prompt the user:
"Open https://servicegraph.co/profile/api-keys, create a key, and add
SERVICEGRAPH_API_KEY=vk_…to.env.localhere (or export it). Tell me when done. Please don't paste the key into chat."
Retry after the user signals ready.
GitHub-search-style.
text
filter := orExpr
orExpr := andExpr ("OR" andExpr)*
andExpr := notExpr (("AND")? notExpr)* # whitespace = implicit AND
notExpr := ("NOT" | "-") notExpr | atom
atom := "(" filter ")" | predicate
predicate:= IDENT op valueOrList | bareword
op := ":" | "=" | ">=" | "<=" | ">" | "<"
valueOrList := value ("," value)*
value := IDENT | NUMBER | tagAtEvidence
tagAtEvidence := IDENT "@" ("low"|"medium"|"high")
bareword := IDENT | NUMBER # → keyword:<bareword>
Four rules that bite: AND binds tighter than OR (use parens);
comma list = OR within one predicate; negation is -x or NOT x;
bareword = keyword search (quote multi-word phrases).
Web-dev examples (validate yours with /check):
text
industry:it_services service_provided:web-development
industry:it_services service_provided:web-development@high state:CA webflow
industry:it_services service_provided:web-development shopify ecommerce
industry:it_services service_provided:web-development wordpress
industry:it_services service_provided:web-development headless next.js
industry:it_services service_provided:web-development@high rating>=4 has:clutch
industry:it_services service_provided:web-development b2b
Platform / vertical / framework → keyword mapping (none are structured tags — keyword them):
User mentionsAdd as keywordWordPresswordpressWebflowwebflowShopify / Shopify PlusshopifySquarespace / Wixsquarespace / wixHeadless / JAMstack / Next.js / Gatsbyheadless, next.js, gatsbySanity / Contentful / Strapisanity, contentful, strapiEcommerce / D2C / DTCecommerce, d2c, dtcB2B / SaaS / fintechb2b, saas, fintech
apex#Firms are identified by their apex domain ( focuslabllc.com, not
www.focuslabllc.com/work).
text
GET /v1/datasets/pro_services/search?filter=industry:it_services+service_provided:web-development&limit=10
# Present, get pick of 3. "Unlocking 3 = 30 credits, 30-day TTL."
POST /v1/datasets/pro_services/unlocks
{ "apexes": ["firm-a.com", "firm-b.com", "firm-c.com"] }
text
GET /v1/datasets/pro_services/search?filter=industry:it_services+service_provided:web-development+webflow+state:CA&limit=10
text
GET /v1/datasets/pro_services/search?filter=industry:it_services+service_provided:web-development+shopify+ecommerce&limit=10
For Shopify Plus, add plus as an additional bareword.
text
GET /v1/datasets/pro_services/search?filter=industry:it_services+service_provided:web-development+wordpress&limit=10
text
GET /v1/datasets/pro_services/search?filter=industry:it_services+service_provided:web-development+headless+next.js&limit=10
If sparse, drop next.js — headless alone captures the architectural pattern.
text
GET /v1/datasets/pro_services/search?filter=industry:it_services+service_provided:web-development&limit=10
Or use the translator:
text
POST /v1/datasets/pro_services/translate-intent
{ "intent": "agency to redesign and rebuild our outdated company site" }
If the user gave a constraint (location, platform, budget proxy via
pricing_model), add it.
text
GET /v1/datasets/pro_services/search?filter=industry:it_services+service_provided:web-development@high+rating>=4+shopify+plus&limit=10
User pastes 8–20 web-dev shop domains:
GET /v1/datasets/pro_services/:apex per domain — free brief
(404 = not in catalog, no charge). A 404 often means the firm
isn't tagged with web-development specifically — it might be in
the catalog under a different tag.POST /unlocks = 10×N credits,
atomic, detail returned.industry:it_services AND service_provided:web-development. Without the industry pin, web-development also appears on some marketing-agency rows; without the service pin, you'd return all IT-services firms.find-software-developer for non-website software. Internal tools, custom CRMs, mobile apps (iOS/Android), backend/API, distributed systems — those are software-developer territory. The boundary: is the end-product a public website, or something else?find-marketing-agency for full marketing engagements. "Build our site AND run our marketing" is broader than this skill — fire find-marketing-agency, which has web-design as a sub-service too.headless cms parses as two AND'd keywords; "headless cms" is one phrase.apex, name, location, ratings. They DON'T include url, phone_primary, email_primary, legal_name, address_full, full platforms — those require an unlock.not_found / not_in_dataset 404 = not in pro_services. Skip; not charged.was_cached:true).JSON envelope: {"error": {"code": "...", "message": "..."}}.
StatusCodeWhat to do400filter_parse_error``position included; fix and re-validate with /check.400kind_in_filterStrip any kind: from filter.400field_not_in_datasetDrop the disallowed field.400invalid_apexRe-normalize.401unauthorized / invalid_audienceRe-prompt for fresh vk_….402insufficient_credits``needed and balance; nothing charged.404not_found / not_in_datasetSkip; not charged.429rate_limitedHonor Retry-After.
User: "Three Webflow agencies in California for our marketing site, ideally with at least a 4-star rating and a Clutch profile."
text
GET /v1/datasets/pro_services/fields?include_values=1
GET /v1/datasets/pro_services/check?filter=industry:it_services+service_provided:web-development@high+webflow+state:CA+rating>=4+has:clutch
GET /v1/datasets/pro_services/search?filter=...&limit=10
# Present briefs. "Unlocking 3 = 30 credits, 30-day TTL."
POST /v1/datasets/pro_services/unlocks
{ "apexes": ["firm-a.com", "firm-b.com", "firm-c.com"] }
GET /v1/me/credits