{"openapi":"3.1.0","info":{"title":"Ripple Ventures public API","summary":"Portfolio, hiring, and Tank Talks endpoints on rippleventures.com","description":"Public, read-only HTTP API for Ripple Ventures (Canadian inception-stage VC). Use it to fetch the portfolio, open roles at portfolio companies, hiring counts, and Tank Talks episodes (JSON + RSS). Official CLI: `npx rippleventures`. Developer index: https://www.rippleventures.com/docs. Navigation for agents: https://www.rippleventures.com/llms.txt. This is not an authenticated product API and does not accept pitches or LP documents.","version":"1.1.0","contact":{"name":"Ripple Ventures","email":"jones@rippleventures.com","url":"https://www.rippleventures.com"},"license":{"name":"Public site data","identifier":"LicenseRef-Public-Site"}},"servers":[{"url":"https://www.rippleventures.com","description":"Production (www)"}],"tags":[{"name":"Portfolio","description":"Firm portfolio as structured JSON"},{"name":"Hiring","description":"Open roles at portfolio companies"},{"name":"Content","description":"Published Ripple Ventures media"},{"name":"Discovery","description":"How agents find and call these operations"}],"paths":{"/portfolio.json":{"get":{"operationId":"listPortfolioCompanies","tags":["Portfolio"],"summary":"List Ripple Ventures portfolio companies","description":"Returns every portfolio company the firm publishes on the homepage: name, domain, founders, fund, stage, geography, and related fields. Prefer this over scraping HTML.","responses":{"200":{"description":"Portfolio snapshot","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortfolioSnapshot"}}}}}}},"/api/portfolio-jobs":{"get":{"operationId":"getPortfolioCompanyJobs","tags":["Hiring"],"summary":"List open roles for one portfolio company","description":"Returns current public jobs for a portfolio company identified by slug. Slug is the company name lowercased with non-alphanumerics turned into hyphens (e.g. Blank Bio → blank-bio). Unknown slugs return 404 JSON. Missing company returns 400 JSON.","parameters":[{"name":"company","in":"query","required":true,"description":"Company slug. Derive it from a name in GET /portfolio.json.","schema":{"type":"string","minLength":1,"examples":["arlo","blank-bio"]}}],"responses":{"200":{"description":"Company plus its open roles","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortfolioJobsResponse"}}}},"400":{"description":"Missing company query parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"No portfolio company matches the slug","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Jobs backend temporarily unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/portfolio-hiring-counts":{"get":{"operationId":"getPortfolioHiringCounts","tags":["Hiring"],"summary":"Open-role counts keyed by company slug","description":"Returns a map of company slug → number of public open roles. Use with GET /portfolio.json to attach counts to company records.","responses":{"200":{"description":"Slug-to-count map","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HiringCountsResponse"}}}},"503":{"description":"Hiring counts temporarily unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/tanktalks-episodes":{"get":{"operationId":"listTankTalksEpisodes","tags":["Content"],"summary":"List Tank Talks episodes for showcase embeds","description":"JSON showcase feed of Tank Talks episodes: title, episode number, published date, duration, thumbnail, Substack URL, and description. Use this to render episode cards without scraping /tanktalks. Filter with ?n=<episode> or ?limit=<count>. The same show also has an RSS feed at /api/tanktalks-feed.","parameters":[{"name":"n","in":"query","required":false,"description":"Episode number (itunes:episode), e.g. 334","schema":{"type":"string","examples":["334"]}},{"name":"limit","in":"query","required":false,"description":"Max episodes to return (1–100). Default is the full snapshot.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"Show metadata plus episode cards","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TankTalksEpisodesResponse"}}}},"400":{"description":"Empty n or invalid limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"No episode matches n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/tanktalks-feed":{"get":{"operationId":"getTankTalksFeed","tags":["Content"],"summary":"Tank Talks podcast RSS feed","description":"Proxies the Tank Talks podcast RSS from Substack. Success is application/rss+xml. Failures are application/json ApiError (502) so agents can parse them.","responses":{"200":{"description":"RSS 2.0 document","content":{"application/rss+xml":{"schema":{"type":"string","description":"RSS XML"}}}},"502":{"description":"Upstream feed missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/openapi.json":{"get":{"operationId":"getOpenApiSpec","tags":["Discovery"],"summary":"This OpenAPI 3.1 document","description":"Machine-readable contract for function calling. Also published as YAML at /api/openapi.yaml.","responses":{"200":{"description":"OpenAPI 3.1 document","content":{"application/json":{"schema":{"type":"object"}}}}}}}},"components":{"schemas":{"ApiError":{"type":"object","additionalProperties":false,"required":["error","code","message","hint"],"description":"Every JSON error from /api/* uses this shape so agents can branch on `code` without scraping HTML.","properties":{"error":{"type":"string","description":"Human-readable message (same text as `message`)."},"code":{"type":"string","description":"Stable machine code: MISSING_COMPANY, COMPANY_NOT_FOUND, UPSTREAM_UNAVAILABLE, NOT_FOUND, JOBS_UNAVAILABLE, HIRING_COUNTS_UNAVAILABLE, INVALID_EPISODE, EPISODE_NOT_FOUND, INVALID_LIMIT."},"message":{"type":"string"},"hint":{"type":"string","description":"What to try next (URL or parameter)."}},"examples":[{"error":"No API endpoint at /api/unknown","code":"NOT_FOUND","message":"No API endpoint at /api/unknown","hint":"See https://www.rippleventures.com/openapi.json for documented operations, or https://www.rippleventures.com/docs for Ripple Ventures developer resources."}]},"PortfolioCompany":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"domain":{"type":["string","null"]},"description":{"type":["string","null"]},"industry":{"type":["array","null"],"items":{"type":"string"}},"geography":{"type":["array","null"],"items":{"type":"string"}},"fund":{"type":["string","null"]},"space":{"type":["string","null"]},"vertical":{"type":["string","null"]},"stage":{"type":["string","null"]},"status":{"type":["string","null"]},"year_invested":{"type":["integer","null"]},"month_invested":{"type":["string","null"]},"press_url":{"type":["string","null"]},"founders":{"type":["array","null"],"items":{"type":"string"}},"founder_linkedins":{"type":["array","null"],"items":{"type":["string","null"]}}}},"PortfolioSnapshot":{"type":"object","required":["firm","site","updated_at","count","companies"],"properties":{"firm":{"type":"string","const":"Ripple Ventures"},"site":{"type":"string","format":"uri"},"updated_at":{"type":"string","format":"date-time"},"count":{"type":"integer"},"companies":{"type":"array","items":{"$ref":"#/components/schemas/PortfolioCompany"}}}},"PortfolioJobCompany":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"domain":{"type":["string","null"]},"jobs_url":{"type":["string","null"]},"description":{"type":["string","null"]},"vertical":{"type":["string","null"]},"industry":{"type":["array","null"],"items":{"type":"string"}},"geography":{"type":["array","null"],"items":{"type":"string"}},"stage":{"type":["string","null"]},"open_roles_count":{"type":"integer"}}},"PortfolioJob":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"job_url":{"type":"string"},"location":{"type":"string"},"posted_display":{"type":["string","null"]},"compensation_display":{"type":["string","null"]},"yoe_display":{"type":"string"}}},"PortfolioJobsResponse":{"type":"object","required":["company","jobs"],"properties":{"company":{"$ref":"#/components/schemas/PortfolioJobCompany"},"jobs":{"type":"array","items":{"$ref":"#/components/schemas/PortfolioJob"}}}},"TankTalksShow":{"type":"object","required":["name","host","site","rss"],"properties":{"name":{"type":"string"},"host":{"type":"string"},"firm":{"type":"string"},"description":{"type":"string"},"site":{"type":"string","format":"uri"},"substack":{"type":"string","format":"uri"},"apple":{"type":"string","format":"uri"},"youtube":{"type":"string","format":"uri"},"spotify":{"type":"string","format":"uri"},"rss":{"type":"string","format":"uri"}}},"TankTalksEpisode":{"type":"object","required":["n","title","published_at","url"],"properties":{"n":{"type":"string","description":"Episode number"},"title":{"type":"string"},"published_at":{"type":"string","description":"RFC-822 date from the RSS feed"},"duration":{"type":"string","examples":["23 min"]},"thumbnail":{"type":["string","null"],"format":"uri","description":"Square cover art (Substack CDN)"},"url":{"type":"string","format":"uri","description":"Episode page on Substack"},"description":{"type":"string"}}},"TankTalksEpisodesResponse":{"type":"object","required":["show","count","episodes"],"properties":{"show":{"$ref":"#/components/schemas/TankTalksShow"},"count":{"type":"integer"},"episodes":{"type":"array","items":{"$ref":"#/components/schemas/TankTalksEpisode"}}}},"HiringCountsResponse":{"type":"object","required":["counts"],"properties":{"counts":{"type":"object","additionalProperties":{"type":"integer"},"description":"Map of company slug to open-role count"}}}}}}