[{"data":1,"prerenderedAt":3688},["ShallowReactive",2],{"navigation":3,"content-tags:articles":146,"tag-api":434},[4],{"title":5,"path":6,"stem":7,"children":8,"page":145},"Articles","\u002Farticles","articles",[9,13,17,21,25,29,33,37,41,45,49,53,57,61,65,69,73,77,81,85,89,93,97,101,105,109,113,117,121,125,129,133,137,141],{"title":10,"path":11,"stem":12},"Building a Claude Code Plugin with Hooks","\u002Farticles\u002F2026-01-04-building-claude-code-plugin-with-hooks","articles\u002F2026-01-04-building-claude-code-plugin-with-hooks",{"title":14,"path":15,"stem":16},"Where should I validate? Everywhere, and for different reasons","\u002Farticles\u002F2026-04-18-validation-across-the-stack","articles\u002F2026-04-18-validation-across-the-stack",{"title":18,"path":19,"stem":20},"Error handling across the stack","\u002Farticles\u002F2026-04-23-error-handling-across-the-stack","articles\u002F2026-04-23-error-handling-across-the-stack",{"title":22,"path":23,"stem":24},"3 Ways to Run NestJS Cron Jobs When Running Multiple Instances","\u002Farticles\u002F3-ways-to-run-nestjs-cron-jobs-when-running-multiple-instances","articles\u002F3-ways-to-run-nestjs-cron-jobs-when-running-multiple-instances",{"title":26,"path":27,"stem":28},"Building a Custom Content Slider (Carousel) in Angular","\u002Farticles\u002Fbuilding-a-custom-content-slider-carousel-in-angular","articles\u002Fbuilding-a-custom-content-slider-carousel-in-angular",{"title":30,"path":31,"stem":32},"Building Jobven: Designing a Developer-Friendly API","\u002Farticles\u002Fbuilding-jobven-designing-a-developer-friendly-api","articles\u002Fbuilding-jobven-designing-a-developer-friendly-api",{"title":34,"path":35,"stem":36},"Building MapleStack: AWS S3 for Data Storage","\u002Farticles\u002Fbuilding-maplestack-aws-s3-for-data-storage","articles\u002Fbuilding-maplestack-aws-s3-for-data-storage",{"title":38,"path":39,"stem":40},"Building MapleStack: Enhancing Email Capabilities with Mailgun","\u002Farticles\u002Fbuilding-maplestack-enhancing-email-capabilities-with-mailgun","articles\u002Fbuilding-maplestack-enhancing-email-capabilities-with-mailgun",{"title":42,"path":43,"stem":44},"Building MapleStack: NestJS for Server-Side Operations","\u002Farticles\u002Fbuilding-maplestack-nestjs-for-server-side-operations","articles\u002Fbuilding-maplestack-nestjs-for-server-side-operations",{"title":46,"path":47,"stem":48},"Building MapleStack: PostgreSQL for Data Storage","\u002Farticles\u002Fbuilding-maplestack-postgresql-for-data-storage","articles\u002Fbuilding-maplestack-postgresql-for-data-storage",{"title":50,"path":51,"stem":52},"Building MapleStack: React for an interactive user interface","\u002Farticles\u002Fbuilding-maplestack-react-for-an-interactive-user-interface","articles\u002Fbuilding-maplestack-react-for-an-interactive-user-interface",{"title":54,"path":55,"stem":56},"Building MapleStack: Securing Connections with Let's Encrypt","\u002Farticles\u002Fbuilding-maplestack-securing-connections-with-lets-encrypt","articles\u002Fbuilding-maplestack-securing-connections-with-lets-encrypt",{"title":58,"path":59,"stem":60},"Building MapleStack: Simplifying User Authorization with AWS Cognito","\u002Farticles\u002Fbuilding-maplestack-simplifying-user-authorization-with-aws-cognito","articles\u002Fbuilding-maplestack-simplifying-user-authorization-with-aws-cognito",{"title":62,"path":63,"stem":64},"Building MapleStack: Tailwind CSS for Streamlined Styling","\u002Farticles\u002Fbuilding-maplestack-tailwind-css-for-streamlined-styling","articles\u002Fbuilding-maplestack-tailwind-css-for-streamlined-styling",{"title":66,"path":67,"stem":68},"Debugging Multiple NestJS Applications in VSCode","\u002Farticles\u002Fdebugging-multiple-nestjs-applications-in-vscode","articles\u002Fdebugging-multiple-nestjs-applications-in-vscode",{"title":70,"path":71,"stem":72},"Enums as arrays in PostgreSQL - Updated 2024","\u002Farticles\u002Fenums-as-arrays-in-postgresql","articles\u002Fenums-as-arrays-in-postgresql",{"title":74,"path":75,"stem":76},"FindChildcare.ca","\u002Farticles\u002Ffindchildcare-ca","articles\u002Ffindchildcare-ca",{"title":78,"path":79,"stem":80},"GraphQL Server - Apollo, KoaJS and Typescript implementation","\u002Farticles\u002Fgraphql-server-apollo-koajs-and-typescript-implementation","articles\u002Fgraphql-server-apollo-koajs-and-typescript-implementation",{"title":82,"path":83,"stem":84},"How I built MapleStack's waitlist with AI: ChatGPT vs Bard","\u002Farticles\u002Fhow-i-built-maplestacks-waitlist-with-ai-chatgpt-vs-bard","articles\u002Fhow-i-built-maplestacks-waitlist-with-ai-chatgpt-vs-bard",{"title":86,"path":87,"stem":88},"How to Integrate Google reCAPTCHA v3 with NestJS in 3 Easy Steps","\u002Farticles\u002Fhow-to-integrate-google-recaptcha-v3-with-nestjs-in-3-easy-steps","articles\u002Fhow-to-integrate-google-recaptcha-v3-with-nestjs-in-3-easy-steps",{"title":90,"path":91,"stem":92},"How to resolve a blacklisted domain","\u002Farticles\u002Fhow-to-resolve-a-blacklisted-domain","articles\u002Fhow-to-resolve-a-blacklisted-domain",{"title":94,"path":95,"stem":96},"Implementing WordPress-like Tags in Nuxt Content","\u002Farticles\u002Fimplementing-wordpress-like-tags-in-nuxt","articles\u002Fimplementing-wordpress-like-tags-in-nuxt",{"title":98,"path":99,"stem":100},"Jobven","\u002Farticles\u002Fjobven","articles\u002Fjobven",{"title":102,"path":103,"stem":104},"KidzLog","\u002Farticles\u002Fkidzlog","articles\u002Fkidzlog",{"title":106,"path":107,"stem":108},"MapleStack","\u002Farticles\u002Fmaplestack","articles\u002Fmaplestack",{"title":110,"path":111,"stem":112},"Migrating from WordPress to Nuxt","\u002Farticles\u002Fmigrating-from-wordpress-to-nuxt","articles\u002Fmigrating-from-wordpress-to-nuxt",{"title":114,"path":115,"stem":116},"My Initial Server Setup Checklist","\u002Farticles\u002Fmy-initial-server-setup-checklist","articles\u002Fmy-initial-server-setup-checklist",{"title":118,"path":119,"stem":120},"Pre-authorized transaction on the Stellar network","\u002Farticles\u002Fpre-authorized-transaction-on-the-stellar-network","articles\u002Fpre-authorized-transaction-on-the-stellar-network",{"title":122,"path":123,"stem":124},"Saza: Open Source mobile and desktop Stellar wallet release.","\u002Farticles\u002Fsaza-open-source-mobile-and-desktop-stellar-wallet-release","articles\u002Fsaza-open-source-mobile-and-desktop-stellar-wallet-release",{"title":126,"path":127,"stem":128},"SpellCheckMySite.com","\u002Farticles\u002Fspellcheckmysite-com","articles\u002Fspellcheckmysite-com",{"title":130,"path":131,"stem":132},"Using Check Constraints in PostgreSQL for Value Validation","\u002Farticles\u002Fusing-check-constraints-in-postgresql-for-value-validation","articles\u002Fusing-check-constraints-in-postgresql-for-value-validation",{"title":134,"path":135,"stem":136},"Using DataLoader in GraphQL","\u002Farticles\u002Fusing-dataloader-in-graphql","articles\u002Fusing-dataloader-in-graphql",{"title":138,"path":139,"stem":140},"Using Typescript in NodeJS development","\u002Farticles\u002Fusing-typescript-in-nodejs-development","articles\u002Fusing-typescript-in-nodejs-development",{"title":142,"path":143,"stem":144},"Welcome!","\u002Farticles\u002Fwelcome","articles\u002Fwelcome",false,[147,158,168,177,185,195,203,212,221,229,237,245,253,261,270,280,288,296,304,312,320,329,337,345,353,361,369,378,386,394,402,410,418,426],{"id":148,"color":149,"description":150,"extension":151,"meta":152,"name":153,"slug":154,"stem":155,"__hash__":156,"count":157},"tags\u002Ftags\u002Fai.yml","purple","Artificial Intelligence and machine learning topics","yml",{},"AI","ai","tags\u002Fai","3Kbwn1GafRMDqK3ErYiBohkqyMbs4y-gmPA5L0htrtI",2,{"id":159,"color":160,"description":161,"extension":151,"meta":162,"name":163,"slug":164,"stem":165,"__hash__":166,"count":167},"tags\u002Ftags\u002Fangular.yml","red","Angular framework tutorials and component development",{},"Angular","angular","tags\u002Fangular","4JdHB81zNrypA0P8D8gi97pj0Ds9E3_IW7YbLvnzs1k",1,{"id":169,"color":170,"description":171,"extension":151,"meta":172,"name":173,"slug":174,"stem":175,"__hash__":176,"count":157},"tags\u002Ftags\u002Fapi.yml","blue","Designing, building and consuming web APIs",{},"API","api","tags\u002Fapi","A0x8MlouPoSEbat0Yj1-_xytY1yBgyVkGIlY-bTzPUM",{"id":178,"color":179,"description":180,"extension":151,"meta":181,"name":5,"slug":7,"stem":182,"__hash__":183,"count":184},"tags\u002Ftags\u002Farticles.yml","gray","General articles and blog posts",{},"tags\u002Farticles","iT5XsTSsj5v9-aLrtgHxDyk_QCYfK8mlea2vVyCdcZM",7,{"id":186,"color":187,"description":188,"extension":151,"meta":189,"name":190,"slug":191,"stem":192,"__hash__":193,"count":194},"tags\u002Ftags\u002Faws.yml","orange","Amazon Web Services cloud platform tutorials and integrations",{},"AWS","aws","tags\u002Faws","i2eOUxfMNR5PbOtUVc7VAbZk5Y7LupicySpVtJY24_A",3,{"id":196,"color":170,"description":197,"extension":151,"meta":198,"name":199,"slug":200,"stem":201,"__hash__":202,"count":167},"tags\u002Ftags\u002Fbard.yml","Google Bard AI assistant",{},"Bard","bard","tags\u002Fbard","uy9j-VgMeapYwgVu98-Df6yqWCXCAwfLn5j8Sb15ZmE",{"id":204,"color":160,"description":205,"extension":151,"meta":206,"name":207,"slug":208,"stem":209,"__hash__":210,"count":211},"tags\u002Ftags\u002Fbuilding-maplestack.yml","Series on building the MapleStack platform",{},"Building MapleStack","building-maplestack","tags\u002Fbuilding-maplestack","hIpKigdKwKbxd8aqva_KMKT-hpxpLDOCnQ9qoRGyjPg",8,{"id":213,"color":214,"description":215,"extension":151,"meta":216,"name":217,"slug":218,"stem":219,"__hash__":220,"count":167},"tags\u002Ftags\u002Fchatgpt.yml","green","OpenAI ChatGPT integration and usage",{},"ChatGPT","chatgpt","tags\u002Fchatgpt","32dptlACBGlEhrLxxih3XBVQeeqayyl3nUn4xrjh6mM",{"id":222,"color":149,"description":223,"extension":151,"meta":224,"name":225,"slug":226,"stem":227,"__hash__":228,"count":167},"tags\u002Ftags\u002Fclaude-code.yml","Building with and extending Anthropic",{},"Claude Code","claude-code","tags\u002Fclaude-code","YxSs-GYpeUzG-p5B9htEndC3aIrB3COkRkKzxd2SoTE",{"id":230,"color":187,"description":231,"extension":151,"meta":232,"name":233,"slug":234,"stem":235,"__hash__":236,"count":167},"tags\u002Ftags\u002Fdataloader.yml","DataLoader utility for batching and caching",{},"DataLoader","dataloader","tags\u002Fdataloader","LmXdDjUElkWFv52Zjjjs9iSl5ho6XzjgngiwhkAnlLk",{"id":238,"color":170,"description":239,"extension":151,"meta":240,"name":241,"slug":242,"stem":243,"__hash__":244,"count":167},"tags\u002Ftags\u002Fdns.yml","Domain Name System configuration and troubleshooting",{},"DNS","dns","tags\u002Fdns","-ouuVgZR4CIiCKyOp51RQoXCKDvhpBV0KMUf1zqLXNc",{"id":246,"color":170,"description":247,"extension":151,"meta":248,"name":249,"slug":250,"stem":251,"__hash__":252,"count":157},"tags\u002Ftags\u002Fengineering.yml","Software engineering practice, craft and technical decision making",{},"Engineering","engineering","tags\u002Fengineering","t3NT39qm9R9NCBT8lwSuG9cxZZZa4Xds-X7AINZw-3o",{"id":254,"color":160,"description":255,"extension":151,"meta":256,"name":257,"slug":258,"stem":259,"__hash__":260,"count":167},"tags\u002Ftags\u002Ferror-handling.yml","Handling, surfacing and recovering from failure across a stack",{},"Error Handling","error-handling","tags\u002Ferror-handling","jxCFis914HceisiNgt920hxUAe4KgCulUZGLzKcNspg",{"id":262,"color":263,"description":264,"extension":151,"meta":265,"name":266,"slug":267,"stem":268,"__hash__":269,"count":157},"tags\u002Ftags\u002Fgraphql.yml","pink","GraphQL API development, schema design, and implementation guides",{},"GraphQL","graphql","tags\u002Fgraphql","1wfWwIg4deU-5JedMVdkw9j9mualhUlJ1MZDgb6V7R0",{"id":271,"color":272,"description":273,"extension":151,"meta":274,"name":275,"slug":276,"stem":277,"__hash__":278,"count":279},"tags\u002Ftags\u002Fjavascript.yml","yellow","JavaScript programming language tutorials and best practices",{},"JavaScript","javascript","tags\u002Fjavascript","qZheXj7oAA3rukxJWCK5SLSe3-_mCwMq0OAi9BtEFRw",6,{"id":281,"color":179,"description":282,"extension":151,"meta":283,"name":284,"slug":285,"stem":286,"__hash__":287,"count":157},"tags\u002Ftags\u002Fkoajs.yml","Koa.js web framework for Node.js",{},"KoaJS","koajs","tags\u002Fkoajs","KVPDsU-wIMo46n6-bdbF-dRkiwlRbz3RJP7K9vSOrmQ",{"id":289,"color":214,"description":290,"extension":151,"meta":291,"name":292,"slug":293,"stem":294,"__hash__":295,"count":167},"tags\u002Ftags\u002Fletsencrypt.yml","Free SSL\u002FTLS certificates with Let's Encrypt",{},"Let's Encrypt","letsencrypt","tags\u002Fletsencrypt","0s8AZbiIKoWGwiCNeWEc_OIo3aDAJlkSB5fjQlgcGNk",{"id":297,"color":179,"description":298,"extension":151,"meta":299,"name":300,"slug":301,"stem":302,"__hash__":303,"count":167},"tags\u002Ftags\u002Flinux.yml","Linux server administration and command-line tools",{},"Linux","linux","tags\u002Flinux","YmvWMX2B2AdXzWfUycxqXt4m9jSgpLE8q7eW6FeiJPk",{"id":305,"color":160,"description":306,"extension":151,"meta":307,"name":106,"slug":308,"stem":309,"__hash__":310,"count":311},"tags\u002Ftags\u002Fmaplestack.yml","Building MapleStack job board platform",{},"maplestack","tags\u002Fmaplestack","v2dZpc6OuxQeK2KTQIC3RYfv4FwNzIAa0VDTzLjWjAA",9,{"id":313,"color":187,"description":314,"extension":151,"meta":315,"name":316,"slug":317,"stem":318,"__hash__":319,"count":167},"tags\u002Ftags\u002Fmigration.yml","Platform and data migration guides",{},"Migration","migration","tags\u002Fmigration","scw_ZTGUersSET1o4l_NAkZ3cDKiplB53LqtFRvE7Yc",{"id":321,"color":160,"description":322,"extension":151,"meta":323,"name":324,"slug":325,"stem":326,"__hash__":327,"count":328},"tags\u002Ftags\u002Fnestjs.yml","Articles about NestJS framework for building efficient, scalable Node.js server-side applications",{},"NestJS","nestjs","tags\u002Fnestjs","GQFi18GrTd6pSmws7rOsyKTbLATtQwywfsZpQwhCsI0",4,{"id":330,"color":214,"description":331,"extension":151,"meta":332,"name":333,"slug":334,"stem":335,"__hash__":336,"count":194},"tags\u002Ftags\u002Fnodejs.yml","Node.js runtime environment and server-side JavaScript",{},"Node.js","nodejs","tags\u002Fnodejs","lusY1Ii4Hd3O6gLdY2NbhHchd3YAT_Q1QOm0BqrfY_4",{"id":338,"color":214,"description":339,"extension":151,"meta":340,"name":341,"slug":342,"stem":343,"__hash__":344,"count":194},"tags\u002Ftags\u002Fnuxt.yml","Nuxt.js framework for Vue.js applications",{},"Nuxt","nuxt","tags\u002Fnuxt","QwQk7HOatKzP3xW3-ie3L7_OJ7O8hbqCT2E8AUtrV2o",{"id":346,"color":187,"description":347,"extension":151,"meta":348,"name":349,"slug":350,"stem":351,"__hash__":352,"count":167},"tags\u002Ftags\u002Fplugins.yml","Extending tools and frameworks through plugin systems",{},"Plugins","plugins","tags\u002Fplugins","gShR9UaVAM-OLItdfQRg89U_mFSX53KgzFge6eQGSaE",{"id":354,"color":170,"description":355,"extension":151,"meta":356,"name":357,"slug":358,"stem":359,"__hash__":360,"count":194},"tags\u002Ftags\u002Fpostgresql.yml","PostgreSQL database tutorials, tips, and best practices",{},"PostgreSQL","postgresql","tags\u002Fpostgresql","o-29A3fv27MHMpunvcXvumOEdfKJJ_Wu6s_Wdej2ynI",{"id":362,"color":149,"description":363,"extension":151,"meta":364,"name":365,"slug":366,"stem":367,"__hash__":368,"count":211},"tags\u002Ftags\u002Fprojects.yml","Articles about personal and professional projects",{},"Projects","projects","tags\u002Fprojects","L834U-TbUh7RZkMdabAGEHgsgGb1hUWpWeIomHD81sE",{"id":370,"color":371,"description":372,"extension":151,"meta":373,"name":374,"slug":375,"stem":376,"__hash__":377,"count":157},"tags\u002Ftags\u002Freact.yml","cyan","React library guides for building user interfaces",{},"React","react","tags\u002Freact","Cl0931Ka3nLB6sYwUevYf3q--ugJfBakfGOEXDpY6Mw",{"id":379,"color":371,"description":380,"extension":151,"meta":381,"name":382,"slug":383,"stem":384,"__hash__":385,"count":167},"tags\u002Ftags\u002Fsaas.yml","Building and running software as a service products",{},"SaaS","saas","tags\u002Fsaas","74u9GtZbJ7IF37YdnFP6JagBgw0PblIZK2mc3r8bbew",{"id":387,"color":170,"description":388,"extension":151,"meta":389,"name":390,"slug":391,"stem":392,"__hash__":393,"count":167},"tags\u002Ftags\u002Fstellar.yml","Stellar blockchain network and cryptocurrency development",{},"Stellar","stellar","tags\u002Fstellar","H71kcSAHbcG_q3Y5cFkgonNN5uH5JxVsXlZjLaotbnk",{"id":395,"color":371,"description":396,"extension":151,"meta":397,"name":398,"slug":399,"stem":400,"__hash__":401,"count":167},"tags\u002Ftags\u002Ftailwindcss.yml","Tailwind CSS utility-first styling framework",{},"Tailwind CSS","tailwindcss","tags\u002Ftailwindcss","_Q7hBntSUWoSWUcYMwmpz1kfCE7RVlSTR9U86k3av_g",{"id":403,"color":170,"description":404,"extension":151,"meta":405,"name":406,"slug":407,"stem":408,"__hash__":409,"count":184},"tags\u002Ftags\u002Ftypescript.yml","TypeScript language features, type safety, and development tips",{},"TypeScript","typescript","tags\u002Ftypescript","zv13aSlv3NhTqdsNRYQ0pKjE5j8rZZk5zv2kD6guG4c",{"id":411,"color":214,"description":412,"extension":151,"meta":413,"name":414,"slug":415,"stem":416,"__hash__":417,"count":167},"tags\u002Ftags\u002Fvalidation.yml","Validating input and data at every layer of an application",{},"Validation","validation","tags\u002Fvalidation","Y2whDaQYOSOUCRjs2TcEedcEvVA8a2b3kq_-xULT3gY",{"id":419,"color":214,"description":420,"extension":151,"meta":421,"name":422,"slug":423,"stem":424,"__hash__":425,"count":167},"tags\u002Ftags\u002Fvps.yml","Virtual Private Server setup and management",{},"VPS","vps","tags\u002Fvps","SOzIPECxOX5g_l0rF3QhJEWwMnOeRe0htUuhWc62PXE",{"id":427,"color":170,"description":428,"extension":151,"meta":429,"name":430,"slug":431,"stem":432,"__hash__":433,"count":157},"tags\u002Ftags\u002Fwordpress.yml","WordPress CMS and related topics",{},"WordPress","wordpress","tags\u002Fwordpress","FiVBFOXCbR45_HKOqwcZAu9dDru5YOemYUcilPwarYQ",{"tag":435,"articles":437,"relatedTags":3681},{"id":169,"color":170,"description":171,"extension":151,"meta":436,"name":173,"slug":174,"stem":175,"__hash__":176},{},[438,3551],{"id":439,"title":30,"author":440,"body":444,"categories":3539,"date":3541,"description":3542,"extension":3543,"head":3544,"image":3545,"meta":3546,"minRead":211,"navigation":1689,"ogImage":3544,"path":31,"robots":3544,"schemaOrg":3544,"seo":3547,"sitemap":3548,"stem":32,"tags":3549,"__hash__":3550},"articles\u002Farticles\u002Fbuilding-jobven-designing-a-developer-friendly-api.md",{"name":441,"avatar":442},"Peter Oliha",{"src":443,"alt":441},"\u002Fassets\u002Fimages\u002Fauthors\u002Fpeter-oliha.jpg",{"type":445,"value":446,"toc":3520},"minimark",[447,458,461,466,469,1217,1220,1233,1239,1245,1254,1258,1261,1305,1312,1325,1328,1332,1335,1340,1347,1379,1383,1386,1424,1431,1435,1438,1470,1473,1476,1508,1515,1519,1556,1560,1563,1778,1782,1785,1791,1797,2219,2224,2228,2235,2760,2763,2767,2770,2778,2781,2861,2864,2868,2871,3194,3197,3201,3204,3459,3462,3466,3469,3475,3484,3490,3494,3497,3513,3516],[448,449,450,451,457],"p",{},"When I started building ",[452,453,98],"a",{"href":454,"rel":455},"https:\u002F\u002Fjobven.com\u002F",[456],"nofollow",", the data collection was the hard part - scraping employer career pages, normalizing job data, keeping everything fresh. But the API that developers would actually use? That needed to be simple.",[448,459,460],{},"I've integrated enough poorly-designed APIs to know what frustrates developers. This is how I approached designing Jobven's public API and the decisions behind it.",[462,463,465],"h2",{"id":464},"start-with-the-response-shape","Start With the Response Shape",[448,467,468],{},"Before writing any endpoint code, I mocked out what I wanted the response to look like. A developer should be able to glance at a response and immediately understand the data:",[470,471,476],"pre",{"className":472,"code":473,"language":474,"meta":475,"style":475},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"data\": [\n    {\n      \"id\": \"abc123-def456-ghi789\",\n      \"title\": \"Frontend Developer\",\n      \"summary\": \"Build beautiful user interfaces for our SaaS platform\",\n      \"companies\": [\n        {\n          \"id\": 123,\n          \"name\": \"TechStartup Inc\",\n          \"website\": \"https:\u002F\u002Ftechstartup.com\"\n        }\n      ],\n      \"locations\": [\n        {\n          \"addressLocality\": \"Austin\",\n          \"addressRegion\": \"Texas\",\n          \"addressCountry\": \"US\",\n          \"workLocation\": \"hybrid\"\n        }\n      ],\n      \"remoteType\": \"hybrid\",\n      \"employmentType\": \"full_time\",\n      \"experienceLevel\": \"mid\",\n      \"salary\": {\n        \"min\": 90000,\n        \"max\": 120000,\n        \"currency\": \"USD\",\n        \"period\": \"yearly\"\n      },\n      \"skills\": {\n        \"primary_skills\": [\"React\", \"TypeScript\", \"CSS\"],\n        \"secondary_skills\": [\"Node.js\", \"GraphQL\"],\n        \"soft_skills\": [\"communication\", \"teamwork\"]\n      },\n      \"postedAt\": 1733097600000,\n      \"status\": \"active\"\n    }\n  ],\n  \"meta\": {\n    \"total\": 15234,\n    \"count\": 5,\n    \"nextCursor\": \"eyJpZCI6ImFiYzEyMyJ9\",\n    \"hasMore\": true,\n    \"requestTimeMs\": 45\n  }\n}\n","json","",[477,478,479,487,505,510,535,556,576,589,594,611,632,652,658,664,678,683,704,725,746,765,770,775,795,816,837,852,870,887,908,927,933,947,989,1019,1052,1057,1074,1093,1099,1105,1119,1137,1154,1175,1190,1205,1211],"code",{"__ignoreMap":475},[480,481,483],"span",{"class":482,"line":167},"line",[480,484,486],{"class":485},"sMK4o","{\n",[480,488,489,492,496,499,502],{"class":482,"line":157},[480,490,491],{"class":485},"  \"",[480,493,495],{"class":494},"spNyl","data",[480,497,498],{"class":485},"\"",[480,500,501],{"class":485},":",[480,503,504],{"class":485}," [\n",[480,506,507],{"class":482,"line":194},[480,508,509],{"class":485},"    {\n",[480,511,512,515,519,521,523,526,530,532],{"class":482,"line":328},[480,513,514],{"class":485},"      \"",[480,516,518],{"class":517},"sBMFI","id",[480,520,498],{"class":485},[480,522,501],{"class":485},[480,524,525],{"class":485}," \"",[480,527,529],{"class":528},"sfazB","abc123-def456-ghi789",[480,531,498],{"class":485},[480,533,534],{"class":485},",\n",[480,536,538,540,543,545,547,549,552,554],{"class":482,"line":537},5,[480,539,514],{"class":485},[480,541,542],{"class":517},"title",[480,544,498],{"class":485},[480,546,501],{"class":485},[480,548,525],{"class":485},[480,550,551],{"class":528},"Frontend Developer",[480,553,498],{"class":485},[480,555,534],{"class":485},[480,557,558,560,563,565,567,569,572,574],{"class":482,"line":279},[480,559,514],{"class":485},[480,561,562],{"class":517},"summary",[480,564,498],{"class":485},[480,566,501],{"class":485},[480,568,525],{"class":485},[480,570,571],{"class":528},"Build beautiful user interfaces for our SaaS platform",[480,573,498],{"class":485},[480,575,534],{"class":485},[480,577,578,580,583,585,587],{"class":482,"line":184},[480,579,514],{"class":485},[480,581,582],{"class":517},"companies",[480,584,498],{"class":485},[480,586,501],{"class":485},[480,588,504],{"class":485},[480,590,591],{"class":482,"line":211},[480,592,593],{"class":485},"        {\n",[480,595,596,599,602,604,606,609],{"class":482,"line":311},[480,597,598],{"class":485},"          \"",[480,600,518],{"class":601},"sbssI",[480,603,498],{"class":485},[480,605,501],{"class":485},[480,607,608],{"class":601}," 123",[480,610,534],{"class":485},[480,612,614,616,619,621,623,625,628,630],{"class":482,"line":613},10,[480,615,598],{"class":485},[480,617,618],{"class":601},"name",[480,620,498],{"class":485},[480,622,501],{"class":485},[480,624,525],{"class":485},[480,626,627],{"class":528},"TechStartup Inc",[480,629,498],{"class":485},[480,631,534],{"class":485},[480,633,635,637,640,642,644,646,649],{"class":482,"line":634},11,[480,636,598],{"class":485},[480,638,639],{"class":601},"website",[480,641,498],{"class":485},[480,643,501],{"class":485},[480,645,525],{"class":485},[480,647,648],{"class":528},"https:\u002F\u002Ftechstartup.com",[480,650,651],{"class":485},"\"\n",[480,653,655],{"class":482,"line":654},12,[480,656,657],{"class":485},"        }\n",[480,659,661],{"class":482,"line":660},13,[480,662,663],{"class":485},"      ],\n",[480,665,667,669,672,674,676],{"class":482,"line":666},14,[480,668,514],{"class":485},[480,670,671],{"class":517},"locations",[480,673,498],{"class":485},[480,675,501],{"class":485},[480,677,504],{"class":485},[480,679,681],{"class":482,"line":680},15,[480,682,593],{"class":485},[480,684,686,688,691,693,695,697,700,702],{"class":482,"line":685},16,[480,687,598],{"class":485},[480,689,690],{"class":601},"addressLocality",[480,692,498],{"class":485},[480,694,501],{"class":485},[480,696,525],{"class":485},[480,698,699],{"class":528},"Austin",[480,701,498],{"class":485},[480,703,534],{"class":485},[480,705,707,709,712,714,716,718,721,723],{"class":482,"line":706},17,[480,708,598],{"class":485},[480,710,711],{"class":601},"addressRegion",[480,713,498],{"class":485},[480,715,501],{"class":485},[480,717,525],{"class":485},[480,719,720],{"class":528},"Texas",[480,722,498],{"class":485},[480,724,534],{"class":485},[480,726,728,730,733,735,737,739,742,744],{"class":482,"line":727},18,[480,729,598],{"class":485},[480,731,732],{"class":601},"addressCountry",[480,734,498],{"class":485},[480,736,501],{"class":485},[480,738,525],{"class":485},[480,740,741],{"class":528},"US",[480,743,498],{"class":485},[480,745,534],{"class":485},[480,747,749,751,754,756,758,760,763],{"class":482,"line":748},19,[480,750,598],{"class":485},[480,752,753],{"class":601},"workLocation",[480,755,498],{"class":485},[480,757,501],{"class":485},[480,759,525],{"class":485},[480,761,762],{"class":528},"hybrid",[480,764,651],{"class":485},[480,766,768],{"class":482,"line":767},20,[480,769,657],{"class":485},[480,771,773],{"class":482,"line":772},21,[480,774,663],{"class":485},[480,776,778,780,783,785,787,789,791,793],{"class":482,"line":777},22,[480,779,514],{"class":485},[480,781,782],{"class":517},"remoteType",[480,784,498],{"class":485},[480,786,501],{"class":485},[480,788,525],{"class":485},[480,790,762],{"class":528},[480,792,498],{"class":485},[480,794,534],{"class":485},[480,796,798,800,803,805,807,809,812,814],{"class":482,"line":797},23,[480,799,514],{"class":485},[480,801,802],{"class":517},"employmentType",[480,804,498],{"class":485},[480,806,501],{"class":485},[480,808,525],{"class":485},[480,810,811],{"class":528},"full_time",[480,813,498],{"class":485},[480,815,534],{"class":485},[480,817,819,821,824,826,828,830,833,835],{"class":482,"line":818},24,[480,820,514],{"class":485},[480,822,823],{"class":517},"experienceLevel",[480,825,498],{"class":485},[480,827,501],{"class":485},[480,829,525],{"class":485},[480,831,832],{"class":528},"mid",[480,834,498],{"class":485},[480,836,534],{"class":485},[480,838,840,842,845,847,849],{"class":482,"line":839},25,[480,841,514],{"class":485},[480,843,844],{"class":517},"salary",[480,846,498],{"class":485},[480,848,501],{"class":485},[480,850,851],{"class":485}," {\n",[480,853,855,858,861,863,865,868],{"class":482,"line":854},26,[480,856,857],{"class":485},"        \"",[480,859,860],{"class":601},"min",[480,862,498],{"class":485},[480,864,501],{"class":485},[480,866,867],{"class":601}," 90000",[480,869,534],{"class":485},[480,871,873,875,878,880,882,885],{"class":482,"line":872},27,[480,874,857],{"class":485},[480,876,877],{"class":601},"max",[480,879,498],{"class":485},[480,881,501],{"class":485},[480,883,884],{"class":601}," 120000",[480,886,534],{"class":485},[480,888,890,892,895,897,899,901,904,906],{"class":482,"line":889},28,[480,891,857],{"class":485},[480,893,894],{"class":601},"currency",[480,896,498],{"class":485},[480,898,501],{"class":485},[480,900,525],{"class":485},[480,902,903],{"class":528},"USD",[480,905,498],{"class":485},[480,907,534],{"class":485},[480,909,911,913,916,918,920,922,925],{"class":482,"line":910},29,[480,912,857],{"class":485},[480,914,915],{"class":601},"period",[480,917,498],{"class":485},[480,919,501],{"class":485},[480,921,525],{"class":485},[480,923,924],{"class":528},"yearly",[480,926,651],{"class":485},[480,928,930],{"class":482,"line":929},30,[480,931,932],{"class":485},"      },\n",[480,934,936,938,941,943,945],{"class":482,"line":935},31,[480,937,514],{"class":485},[480,939,940],{"class":517},"skills",[480,942,498],{"class":485},[480,944,501],{"class":485},[480,946,851],{"class":485},[480,948,950,952,955,957,959,962,964,966,968,971,973,975,977,979,981,984,986],{"class":482,"line":949},32,[480,951,857],{"class":485},[480,953,954],{"class":601},"primary_skills",[480,956,498],{"class":485},[480,958,501],{"class":485},[480,960,961],{"class":485}," [",[480,963,498],{"class":485},[480,965,374],{"class":528},[480,967,498],{"class":485},[480,969,970],{"class":485},",",[480,972,525],{"class":485},[480,974,406],{"class":528},[480,976,498],{"class":485},[480,978,970],{"class":485},[480,980,525],{"class":485},[480,982,983],{"class":528},"CSS",[480,985,498],{"class":485},[480,987,988],{"class":485},"],\n",[480,990,992,994,997,999,1001,1003,1005,1007,1009,1011,1013,1015,1017],{"class":482,"line":991},33,[480,993,857],{"class":485},[480,995,996],{"class":601},"secondary_skills",[480,998,498],{"class":485},[480,1000,501],{"class":485},[480,1002,961],{"class":485},[480,1004,498],{"class":485},[480,1006,333],{"class":528},[480,1008,498],{"class":485},[480,1010,970],{"class":485},[480,1012,525],{"class":485},[480,1014,266],{"class":528},[480,1016,498],{"class":485},[480,1018,988],{"class":485},[480,1020,1022,1024,1027,1029,1031,1033,1035,1038,1040,1042,1044,1047,1049],{"class":482,"line":1021},34,[480,1023,857],{"class":485},[480,1025,1026],{"class":601},"soft_skills",[480,1028,498],{"class":485},[480,1030,501],{"class":485},[480,1032,961],{"class":485},[480,1034,498],{"class":485},[480,1036,1037],{"class":528},"communication",[480,1039,498],{"class":485},[480,1041,970],{"class":485},[480,1043,525],{"class":485},[480,1045,1046],{"class":528},"teamwork",[480,1048,498],{"class":485},[480,1050,1051],{"class":485},"]\n",[480,1053,1055],{"class":482,"line":1054},35,[480,1056,932],{"class":485},[480,1058,1060,1062,1065,1067,1069,1072],{"class":482,"line":1059},36,[480,1061,514],{"class":485},[480,1063,1064],{"class":517},"postedAt",[480,1066,498],{"class":485},[480,1068,501],{"class":485},[480,1070,1071],{"class":601}," 1733097600000",[480,1073,534],{"class":485},[480,1075,1077,1079,1082,1084,1086,1088,1091],{"class":482,"line":1076},37,[480,1078,514],{"class":485},[480,1080,1081],{"class":517},"status",[480,1083,498],{"class":485},[480,1085,501],{"class":485},[480,1087,525],{"class":485},[480,1089,1090],{"class":528},"active",[480,1092,651],{"class":485},[480,1094,1096],{"class":482,"line":1095},38,[480,1097,1098],{"class":485},"    }\n",[480,1100,1102],{"class":482,"line":1101},39,[480,1103,1104],{"class":485},"  ],\n",[480,1106,1108,1110,1113,1115,1117],{"class":482,"line":1107},40,[480,1109,491],{"class":485},[480,1111,1112],{"class":494},"meta",[480,1114,498],{"class":485},[480,1116,501],{"class":485},[480,1118,851],{"class":485},[480,1120,1122,1125,1128,1130,1132,1135],{"class":482,"line":1121},41,[480,1123,1124],{"class":485},"    \"",[480,1126,1127],{"class":517},"total",[480,1129,498],{"class":485},[480,1131,501],{"class":485},[480,1133,1134],{"class":601}," 15234",[480,1136,534],{"class":485},[480,1138,1140,1142,1145,1147,1149,1152],{"class":482,"line":1139},42,[480,1141,1124],{"class":485},[480,1143,1144],{"class":517},"count",[480,1146,498],{"class":485},[480,1148,501],{"class":485},[480,1150,1151],{"class":601}," 5",[480,1153,534],{"class":485},[480,1155,1157,1159,1162,1164,1166,1168,1171,1173],{"class":482,"line":1156},43,[480,1158,1124],{"class":485},[480,1160,1161],{"class":517},"nextCursor",[480,1163,498],{"class":485},[480,1165,501],{"class":485},[480,1167,525],{"class":485},[480,1169,1170],{"class":528},"eyJpZCI6ImFiYzEyMyJ9",[480,1172,498],{"class":485},[480,1174,534],{"class":485},[480,1176,1178,1180,1183,1185,1187],{"class":482,"line":1177},44,[480,1179,1124],{"class":485},[480,1181,1182],{"class":517},"hasMore",[480,1184,498],{"class":485},[480,1186,501],{"class":485},[480,1188,1189],{"class":485}," true,\n",[480,1191,1193,1195,1198,1200,1202],{"class":482,"line":1192},45,[480,1194,1124],{"class":485},[480,1196,1197],{"class":517},"requestTimeMs",[480,1199,498],{"class":485},[480,1201,501],{"class":485},[480,1203,1204],{"class":601}," 45\n",[480,1206,1208],{"class":482,"line":1207},46,[480,1209,1210],{"class":485},"  }\n",[480,1212,1214],{"class":482,"line":1213},47,[480,1215,1216],{"class":485},"}\n",[448,1218,1219],{},"A few deliberate choices here:",[448,1221,1222,1226,1227,1229,1230,1232],{},[1223,1224,1225],"strong",{},"Data and meta separation"," - The actual jobs are in ",[477,1228,495],{},", pagination info is in ",[477,1231,1112],{},". Clean separation that every modern API follows.",[448,1234,1235,1238],{},[1223,1236,1237],{},"Arrays for companies and locations"," - Some jobs have multiple hiring companies or locations. Using arrays from the start means developers don't need to handle both single values and arrays.",[448,1240,1241,1244],{},[1223,1242,1243],{},"Nested skills object"," - Rather than a flat array, skills are categorized. This took extra work on my end to classify, but it makes the data more useful for filtering and display.",[448,1246,1247,1250,1251,1253],{},[1223,1248,1249],{},"Unix timestamps"," - ",[477,1252,1064],{}," is a millisecond timestamp. No timezone ambiguity, easy to work with in any language.",[462,1255,1257],{"id":1256},"authentication-keep-it-simple","Authentication: Keep It Simple",[448,1259,1260],{},"I went with API key authentication via header:",[470,1262,1266],{"className":1263,"code":1264,"language":1265,"meta":475,"style":475},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","curl -X GET 'https:\u002F\u002Fapi.jobven.com\u002Fv1\u002Fpublic\u002Fjobs?limit=5' \\\n  -H 'X-API-Key: your_api_key'\n","bash",[477,1267,1268,1292],{"__ignoreMap":475},[480,1269,1270,1273,1276,1279,1282,1285,1288],{"class":482,"line":167},[480,1271,1272],{"class":517},"curl",[480,1274,1275],{"class":528}," -X",[480,1277,1278],{"class":528}," GET",[480,1280,1281],{"class":485}," '",[480,1283,1284],{"class":528},"https:\u002F\u002Fapi.jobven.com\u002Fv1\u002Fpublic\u002Fjobs?limit=5",[480,1286,1287],{"class":485},"'",[480,1289,1291],{"class":1290},"sTEyZ"," \\\n",[480,1293,1294,1297,1299,1302],{"class":482,"line":157},[480,1295,1296],{"class":528},"  -H",[480,1298,1281],{"class":485},[480,1300,1301],{"class":528},"X-API-Key: your_api_key",[480,1303,1304],{"class":485},"'\n",[448,1306,1307,1308,1311],{},"OAuth would be overkill for a data API. JWT adds complexity. A simple API key in the ",[477,1309,1310],{},"X-API-Key"," header is:",[1313,1314,1315,1319,1322],"ul",{},[1316,1317,1318],"li",{},"Easy to generate and revoke from a dashboard",[1316,1320,1321],{},"Works identically in every HTTP client",[1316,1323,1324],{},"No token refresh flows to implement",[448,1326,1327],{},"The key identifies the account, enforces rate limits, and tracks usage. That's all I need.",[462,1329,1331],{"id":1330},"filtering-be-generous","Filtering: Be Generous",[448,1333,1334],{},"The filtering system was where I spent the most design time. Developers building job boards need precise control over what jobs they fetch.",[1336,1337,1339],"h3",{"id":1338},"keyword-search","Keyword Search",[448,1341,1342,1343,1346],{},"The ",[477,1344,1345],{},"q"," parameter searches across title, summary, and description:",[470,1348,1350],{"className":1263,"code":1349,"language":1265,"meta":475,"style":475},"curl -X GET 'https:\u002F\u002Fapi.jobven.com\u002Fv1\u002Fpublic\u002Fjobs?q=react+developer&limit=20' \\\n  -H 'X-API-Key: your_api_key'\n",[477,1351,1352,1369],{"__ignoreMap":475},[480,1353,1354,1356,1358,1360,1362,1365,1367],{"class":482,"line":167},[480,1355,1272],{"class":517},[480,1357,1275],{"class":528},[480,1359,1278],{"class":528},[480,1361,1281],{"class":485},[480,1363,1364],{"class":528},"https:\u002F\u002Fapi.jobven.com\u002Fv1\u002Fpublic\u002Fjobs?q=react+developer&limit=20",[480,1366,1287],{"class":485},[480,1368,1291],{"class":1290},[480,1370,1371,1373,1375,1377],{"class":482,"line":157},[480,1372,1296],{"class":528},[480,1374,1281],{"class":485},[480,1376,1301],{"class":528},[480,1378,1304],{"class":485},[1336,1380,1382],{"id":1381},"remote-type","Remote Type",[448,1384,1385],{},"Remote work is a first-class filter, not buried in location:",[470,1387,1389],{"className":1263,"code":1388,"language":1265,"meta":475,"style":475},"# Options: remote, hybrid, onsite, flexible\ncurl -X GET 'https:\u002F\u002Fapi.jobven.com\u002Fv1\u002Fpublic\u002Fjobs?remoteType[]=remote' \\\n  -H 'X-API-Key: your_api_key'\n",[477,1390,1391,1397,1414],{"__ignoreMap":475},[480,1392,1393],{"class":482,"line":167},[480,1394,1396],{"class":1395},"sHwdD","# Options: remote, hybrid, onsite, flexible\n",[480,1398,1399,1401,1403,1405,1407,1410,1412],{"class":482,"line":157},[480,1400,1272],{"class":517},[480,1402,1275],{"class":528},[480,1404,1278],{"class":528},[480,1406,1281],{"class":485},[480,1408,1409],{"class":528},"https:\u002F\u002Fapi.jobven.com\u002Fv1\u002Fpublic\u002Fjobs?remoteType[]=remote",[480,1411,1287],{"class":485},[480,1413,1291],{"class":1290},[480,1415,1416,1418,1420,1422],{"class":482,"line":194},[480,1417,1296],{"class":528},[480,1419,1281],{"class":485},[480,1421,1301],{"class":528},[480,1423,1304],{"class":485},[448,1425,1426,1427,1430],{},"The array syntax (",[477,1428,1429],{},"remoteType[]",") lets you filter for multiple types in one request.",[1336,1432,1434],{"id":1433},"location","Location",[448,1436,1437],{},"Filter by country, region, or city:",[470,1439,1441],{"className":1263,"code":1440,"language":1265,"meta":475,"style":475},"curl -X GET 'https:\u002F\u002Fapi.jobven.com\u002Fv1\u002Fpublic\u002Fjobs?country=US&location=San+Francisco' \\\n  -H 'X-API-Key: your_api_key'\n",[477,1442,1443,1460],{"__ignoreMap":475},[480,1444,1445,1447,1449,1451,1453,1456,1458],{"class":482,"line":167},[480,1446,1272],{"class":517},[480,1448,1275],{"class":528},[480,1450,1278],{"class":528},[480,1452,1281],{"class":485},[480,1454,1455],{"class":528},"https:\u002F\u002Fapi.jobven.com\u002Fv1\u002Fpublic\u002Fjobs?country=US&location=San+Francisco",[480,1457,1287],{"class":485},[480,1459,1291],{"class":1290},[480,1461,1462,1464,1466,1468],{"class":482,"line":157},[480,1463,1296],{"class":528},[480,1465,1281],{"class":485},[480,1467,1301],{"class":528},[480,1469,1304],{"class":485},[1336,1471,1472],{"id":940},"Skills",[448,1474,1475],{},"Comma-separated skill filtering:",[470,1477,1479],{"className":1263,"code":1478,"language":1265,"meta":475,"style":475},"curl -X GET 'https:\u002F\u002Fapi.jobven.com\u002Fv1\u002Fpublic\u002Fjobs?skills=python,aws,kubernetes' \\\n  -H 'X-API-Key: your_api_key'\n",[477,1480,1481,1498],{"__ignoreMap":475},[480,1482,1483,1485,1487,1489,1491,1494,1496],{"class":482,"line":167},[480,1484,1272],{"class":517},[480,1486,1275],{"class":528},[480,1488,1278],{"class":528},[480,1490,1281],{"class":485},[480,1492,1493],{"class":528},"https:\u002F\u002Fapi.jobven.com\u002Fv1\u002Fpublic\u002Fjobs?skills=python,aws,kubernetes",[480,1495,1287],{"class":485},[480,1497,1291],{"class":1290},[480,1499,1500,1502,1504,1506],{"class":482,"line":157},[480,1501,1296],{"class":528},[480,1503,1281],{"class":485},[480,1505,1301],{"class":528},[480,1507,1304],{"class":485},[448,1509,1510,1511,1514],{},"This returns jobs matching any of these skills. I considered adding ",[477,1512,1513],{},"skillsMatch=all"," for AND logic, but kept it simple for v1.",[1336,1516,1518],{"id":1517},"experience-level","Experience Level",[470,1520,1522],{"className":1263,"code":1521,"language":1265,"meta":475,"style":475},"# Options: entry, mid, senior, lead, executive\ncurl -X GET 'https:\u002F\u002Fapi.jobven.com\u002Fv1\u002Fpublic\u002Fjobs?experienceLevel=senior' \\\n  -H 'X-API-Key: your_api_key'\n",[477,1523,1524,1529,1546],{"__ignoreMap":475},[480,1525,1526],{"class":482,"line":167},[480,1527,1528],{"class":1395},"# Options: entry, mid, senior, lead, executive\n",[480,1530,1531,1533,1535,1537,1539,1542,1544],{"class":482,"line":157},[480,1532,1272],{"class":517},[480,1534,1275],{"class":528},[480,1536,1278],{"class":528},[480,1538,1281],{"class":485},[480,1540,1541],{"class":528},"https:\u002F\u002Fapi.jobven.com\u002Fv1\u002Fpublic\u002Fjobs?experienceLevel=senior",[480,1543,1287],{"class":485},[480,1545,1291],{"class":1290},[480,1547,1548,1550,1552,1554],{"class":482,"line":194},[480,1549,1296],{"class":528},[480,1551,1281],{"class":485},[480,1553,1301],{"class":528},[480,1555,1304],{"class":485},[1336,1557,1559],{"id":1558},"combining-filters","Combining Filters",[448,1561,1562],{},"All filters can be combined:",[470,1564,1567],{"className":1565,"code":1566,"language":276,"meta":475,"style":475},"language-javascript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","const params = new URLSearchParams({\n  q: 'backend engineer',\n  'remoteType[]': 'remote',\n  skills: 'python,postgresql',\n  experienceLevel: 'mid',\n  limit: '25'\n});\n\nconst response = await fetch(\n  `https:\u002F\u002Fapi.jobven.com\u002Fv1\u002Fpublic\u002Fjobs?${params}`,\n  {\n    headers: { 'X-API-Key': process.env.JOBVEN_API_KEY }\n  }\n);\n",[477,1568,1569,1592,1609,1629,1645,1660,1674,1685,1691,1710,1729,1734,1768,1772],{"__ignoreMap":475},[480,1570,1571,1574,1577,1580,1583,1587,1590],{"class":482,"line":167},[480,1572,1573],{"class":494},"const",[480,1575,1576],{"class":1290}," params ",[480,1578,1579],{"class":485},"=",[480,1581,1582],{"class":485}," new",[480,1584,1586],{"class":1585},"s2Zo4"," URLSearchParams",[480,1588,1589],{"class":1290},"(",[480,1591,486],{"class":485},[480,1593,1594,1598,1600,1602,1605,1607],{"class":482,"line":157},[480,1595,1597],{"class":1596},"swJcz","  q",[480,1599,501],{"class":485},[480,1601,1281],{"class":485},[480,1603,1604],{"class":528},"backend engineer",[480,1606,1287],{"class":485},[480,1608,534],{"class":485},[480,1610,1611,1614,1616,1618,1620,1622,1625,1627],{"class":482,"line":194},[480,1612,1613],{"class":485},"  '",[480,1615,1429],{"class":1596},[480,1617,1287],{"class":485},[480,1619,501],{"class":485},[480,1621,1281],{"class":485},[480,1623,1624],{"class":528},"remote",[480,1626,1287],{"class":485},[480,1628,534],{"class":485},[480,1630,1631,1634,1636,1638,1641,1643],{"class":482,"line":328},[480,1632,1633],{"class":1596},"  skills",[480,1635,501],{"class":485},[480,1637,1281],{"class":485},[480,1639,1640],{"class":528},"python,postgresql",[480,1642,1287],{"class":485},[480,1644,534],{"class":485},[480,1646,1647,1650,1652,1654,1656,1658],{"class":482,"line":537},[480,1648,1649],{"class":1596},"  experienceLevel",[480,1651,501],{"class":485},[480,1653,1281],{"class":485},[480,1655,832],{"class":528},[480,1657,1287],{"class":485},[480,1659,534],{"class":485},[480,1661,1662,1665,1667,1669,1672],{"class":482,"line":279},[480,1663,1664],{"class":1596},"  limit",[480,1666,501],{"class":485},[480,1668,1281],{"class":485},[480,1670,1671],{"class":528},"25",[480,1673,1304],{"class":485},[480,1675,1676,1679,1682],{"class":482,"line":184},[480,1677,1678],{"class":485},"}",[480,1680,1681],{"class":1290},")",[480,1683,1684],{"class":485},";\n",[480,1686,1687],{"class":482,"line":211},[480,1688,1690],{"emptyLinePlaceholder":1689},true,"\n",[480,1692,1693,1695,1698,1700,1704,1707],{"class":482,"line":311},[480,1694,1573],{"class":494},[480,1696,1697],{"class":1290}," response ",[480,1699,1579],{"class":485},[480,1701,1703],{"class":1702},"s7zQu"," await",[480,1705,1706],{"class":1585}," fetch",[480,1708,1709],{"class":1290},"(\n",[480,1711,1712,1715,1718,1721,1724,1727],{"class":482,"line":613},[480,1713,1714],{"class":485},"  `",[480,1716,1717],{"class":528},"https:\u002F\u002Fapi.jobven.com\u002Fv1\u002Fpublic\u002Fjobs?",[480,1719,1720],{"class":485},"${",[480,1722,1723],{"class":1290},"params",[480,1725,1726],{"class":485},"}`",[480,1728,534],{"class":485},[480,1730,1731],{"class":482,"line":634},[480,1732,1733],{"class":485},"  {\n",[480,1735,1736,1739,1741,1744,1746,1748,1750,1752,1755,1758,1761,1763,1766],{"class":482,"line":654},[480,1737,1738],{"class":1596},"    headers",[480,1740,501],{"class":485},[480,1742,1743],{"class":485}," {",[480,1745,1281],{"class":485},[480,1747,1310],{"class":1596},[480,1749,1287],{"class":485},[480,1751,501],{"class":485},[480,1753,1754],{"class":1290}," process",[480,1756,1757],{"class":485},".",[480,1759,1760],{"class":1290},"env",[480,1762,1757],{"class":485},[480,1764,1765],{"class":1290},"JOBVEN_API_KEY ",[480,1767,1216],{"class":485},[480,1769,1770],{"class":482,"line":660},[480,1771,1210],{"class":485},[480,1773,1774,1776],{"class":482,"line":666},[480,1775,1681],{"class":1290},[480,1777,1684],{"class":485},[462,1779,1781],{"id":1780},"pagination-cursors-over-offsets","Pagination: Cursors Over Offsets",[448,1783,1784],{},"I chose cursor-based pagination over offset-based. Here's why:",[448,1786,1787,1790],{},[1223,1788,1789],{},"Offset pagination breaks with changing data",". If you're on page 2 and new jobs get added, page 3 might include duplicates or skip items. With thousands of jobs being added and removed daily, this matters.",[448,1792,1793,1796],{},[1223,1794,1795],{},"Cursor pagination is consistent",". The cursor encodes where you left off, so you always get the next set of results regardless of what changed.",[470,1798,1800],{"className":1565,"code":1799,"language":276,"meta":475,"style":475},"async function getAllJobs(filters = {}) {\n  const jobs = [];\n  let cursor = null;\n  let hasMore = true;\n\n  while (hasMore) {\n    const params = new URLSearchParams({\n      ...filters,\n      limit: '100',\n      ...(cursor && { cursor })\n    });\n\n    const response = await fetch(\n      `https:\u002F\u002Fapi.jobven.com\u002Fv1\u002Fpublic\u002Fjobs?${params}`,\n      {\n        headers: { 'X-API-Key': process.env.JOBVEN_API_KEY }\n      }\n    );\n\n    const data = await response.json();\n    jobs.push(...data.data);\n\n    cursor = data.meta.nextCursor;\n    hasMore = data.meta.hasMore;\n\n    console.log(`Fetched ${jobs.length} of ${data.meta.total} jobs...`);\n  }\n\n  return jobs;\n}\n",[477,1801,1802,1827,1842,1855,1870,1874,1889,1907,1916,1932,1954,1963,1967,1982,1997,2002,2033,2038,2045,2049,2071,2096,2100,2119,2138,2142,2198,2202,2206,2215],{"__ignoreMap":475},[480,1803,1804,1807,1810,1813,1815,1819,1822,1825],{"class":482,"line":167},[480,1805,1806],{"class":494},"async",[480,1808,1809],{"class":494}," function",[480,1811,1812],{"class":1585}," getAllJobs",[480,1814,1589],{"class":485},[480,1816,1818],{"class":1817},"sHdIc","filters",[480,1820,1821],{"class":485}," =",[480,1823,1824],{"class":485}," {})",[480,1826,851],{"class":485},[480,1828,1829,1832,1835,1837,1840],{"class":482,"line":157},[480,1830,1831],{"class":494},"  const",[480,1833,1834],{"class":1290}," jobs",[480,1836,1821],{"class":485},[480,1838,1839],{"class":1596}," []",[480,1841,1684],{"class":485},[480,1843,1844,1847,1850,1852],{"class":482,"line":194},[480,1845,1846],{"class":494},"  let",[480,1848,1849],{"class":1290}," cursor",[480,1851,1821],{"class":485},[480,1853,1854],{"class":485}," null;\n",[480,1856,1857,1859,1862,1864,1868],{"class":482,"line":328},[480,1858,1846],{"class":494},[480,1860,1861],{"class":1290}," hasMore",[480,1863,1821],{"class":485},[480,1865,1867],{"class":1866},"sfNiH"," true",[480,1869,1684],{"class":485},[480,1871,1872],{"class":482,"line":537},[480,1873,1690],{"emptyLinePlaceholder":1689},[480,1875,1876,1879,1882,1884,1887],{"class":482,"line":279},[480,1877,1878],{"class":1702},"  while",[480,1880,1881],{"class":1596}," (",[480,1883,1182],{"class":1290},[480,1885,1886],{"class":1596},") ",[480,1888,486],{"class":485},[480,1890,1891,1894,1897,1899,1901,1903,1905],{"class":482,"line":184},[480,1892,1893],{"class":494},"    const",[480,1895,1896],{"class":1290}," params",[480,1898,1821],{"class":485},[480,1900,1582],{"class":485},[480,1902,1586],{"class":1585},[480,1904,1589],{"class":1596},[480,1906,486],{"class":485},[480,1908,1909,1912,1914],{"class":482,"line":211},[480,1910,1911],{"class":485},"      ...",[480,1913,1818],{"class":1290},[480,1915,534],{"class":485},[480,1917,1918,1921,1923,1925,1928,1930],{"class":482,"line":311},[480,1919,1920],{"class":1596},"      limit",[480,1922,501],{"class":485},[480,1924,1281],{"class":485},[480,1926,1927],{"class":528},"100",[480,1929,1287],{"class":485},[480,1931,534],{"class":485},[480,1933,1934,1936,1938,1941,1944,1946,1948,1951],{"class":482,"line":613},[480,1935,1911],{"class":485},[480,1937,1589],{"class":1596},[480,1939,1940],{"class":1290},"cursor",[480,1942,1943],{"class":485}," &&",[480,1945,1743],{"class":485},[480,1947,1849],{"class":1290},[480,1949,1950],{"class":485}," }",[480,1952,1953],{"class":1596},")\n",[480,1955,1956,1959,1961],{"class":482,"line":634},[480,1957,1958],{"class":485},"    }",[480,1960,1681],{"class":1596},[480,1962,1684],{"class":485},[480,1964,1965],{"class":482,"line":654},[480,1966,1690],{"emptyLinePlaceholder":1689},[480,1968,1969,1971,1974,1976,1978,1980],{"class":482,"line":660},[480,1970,1893],{"class":494},[480,1972,1973],{"class":1290}," response",[480,1975,1821],{"class":485},[480,1977,1703],{"class":1702},[480,1979,1706],{"class":1585},[480,1981,1709],{"class":1596},[480,1983,1984,1987,1989,1991,1993,1995],{"class":482,"line":666},[480,1985,1986],{"class":485},"      `",[480,1988,1717],{"class":528},[480,1990,1720],{"class":485},[480,1992,1723],{"class":1290},[480,1994,1726],{"class":485},[480,1996,534],{"class":485},[480,1998,1999],{"class":482,"line":680},[480,2000,2001],{"class":485},"      {\n",[480,2003,2004,2007,2009,2011,2013,2015,2017,2019,2021,2023,2025,2027,2030],{"class":482,"line":685},[480,2005,2006],{"class":1596},"        headers",[480,2008,501],{"class":485},[480,2010,1743],{"class":485},[480,2012,1281],{"class":485},[480,2014,1310],{"class":1596},[480,2016,1287],{"class":485},[480,2018,501],{"class":485},[480,2020,1754],{"class":1290},[480,2022,1757],{"class":485},[480,2024,1760],{"class":1290},[480,2026,1757],{"class":485},[480,2028,2029],{"class":1290},"JOBVEN_API_KEY",[480,2031,2032],{"class":485}," }\n",[480,2034,2035],{"class":482,"line":706},[480,2036,2037],{"class":485},"      }\n",[480,2039,2040,2043],{"class":482,"line":727},[480,2041,2042],{"class":1596},"    )",[480,2044,1684],{"class":485},[480,2046,2047],{"class":482,"line":748},[480,2048,1690],{"emptyLinePlaceholder":1689},[480,2050,2051,2053,2056,2058,2060,2062,2064,2066,2069],{"class":482,"line":767},[480,2052,1893],{"class":494},[480,2054,2055],{"class":1290}," data",[480,2057,1821],{"class":485},[480,2059,1703],{"class":1702},[480,2061,1973],{"class":1290},[480,2063,1757],{"class":485},[480,2065,474],{"class":1585},[480,2067,2068],{"class":1596},"()",[480,2070,1684],{"class":485},[480,2072,2073,2076,2078,2081,2083,2086,2088,2090,2092,2094],{"class":482,"line":772},[480,2074,2075],{"class":1290},"    jobs",[480,2077,1757],{"class":485},[480,2079,2080],{"class":1585},"push",[480,2082,1589],{"class":1596},[480,2084,2085],{"class":485},"...",[480,2087,495],{"class":1290},[480,2089,1757],{"class":485},[480,2091,495],{"class":1290},[480,2093,1681],{"class":1596},[480,2095,1684],{"class":485},[480,2097,2098],{"class":482,"line":777},[480,2099,1690],{"emptyLinePlaceholder":1689},[480,2101,2102,2105,2107,2109,2111,2113,2115,2117],{"class":482,"line":797},[480,2103,2104],{"class":1290},"    cursor",[480,2106,1821],{"class":485},[480,2108,2055],{"class":1290},[480,2110,1757],{"class":485},[480,2112,1112],{"class":1290},[480,2114,1757],{"class":485},[480,2116,1161],{"class":1290},[480,2118,1684],{"class":485},[480,2120,2121,2124,2126,2128,2130,2132,2134,2136],{"class":482,"line":818},[480,2122,2123],{"class":1290},"    hasMore",[480,2125,1821],{"class":485},[480,2127,2055],{"class":1290},[480,2129,1757],{"class":485},[480,2131,1112],{"class":1290},[480,2133,1757],{"class":485},[480,2135,1182],{"class":1290},[480,2137,1684],{"class":485},[480,2139,2140],{"class":482,"line":839},[480,2141,1690],{"emptyLinePlaceholder":1689},[480,2143,2144,2147,2149,2152,2154,2157,2160,2162,2165,2167,2170,2172,2175,2177,2179,2181,2183,2185,2187,2189,2192,2194,2196],{"class":482,"line":854},[480,2145,2146],{"class":1290},"    console",[480,2148,1757],{"class":485},[480,2150,2151],{"class":1585},"log",[480,2153,1589],{"class":1596},[480,2155,2156],{"class":485},"`",[480,2158,2159],{"class":528},"Fetched ",[480,2161,1720],{"class":485},[480,2163,2164],{"class":1290},"jobs",[480,2166,1757],{"class":485},[480,2168,2169],{"class":1290},"length",[480,2171,1678],{"class":485},[480,2173,2174],{"class":528}," of ",[480,2176,1720],{"class":485},[480,2178,495],{"class":1290},[480,2180,1757],{"class":485},[480,2182,1112],{"class":1290},[480,2184,1757],{"class":485},[480,2186,1127],{"class":1290},[480,2188,1678],{"class":485},[480,2190,2191],{"class":528}," jobs...",[480,2193,2156],{"class":485},[480,2195,1681],{"class":1596},[480,2197,1684],{"class":485},[480,2199,2200],{"class":482,"line":872},[480,2201,1210],{"class":485},[480,2203,2204],{"class":482,"line":889},[480,2205,1690],{"emptyLinePlaceholder":1689},[480,2207,2208,2211,2213],{"class":482,"line":910},[480,2209,2210],{"class":1702},"  return",[480,2212,1834],{"class":1290},[480,2214,1684],{"class":485},[480,2216,2217],{"class":482,"line":929},[480,2218,1216],{"class":485},[448,2220,1342,2221,2223],{},[477,2222,1112],{}," object tells you everything: total count, whether there's more data, and the cursor for the next page.",[462,2225,2227],{"id":2226},"incremental-sync-the-feature-that-matters-most","Incremental Sync: The Feature That Matters Most",[448,2229,2230,2231,2234],{},"For production applications, fetching all jobs every time is wasteful. The ",[477,2232,2233],{},"postedAfter"," parameter enables incremental sync:",[470,2236,2238],{"className":1565,"code":2237,"language":276,"meta":475,"style":475},"async function syncNewJobs(lastSyncTimestamp) {\n  const params = new URLSearchParams({\n    postedAfter: lastSyncTimestamp.toString(),\n    limit: '100'\n  });\n\n  const response = await fetch(\n    `https:\u002F\u002Fapi.jobven.com\u002Fv1\u002Fpublic\u002Fjobs?${params}`,\n    {\n      headers: { 'X-API-Key': process.env.JOBVEN_API_KEY }\n    }\n  );\n\n  const { data: newJobs, meta } = await response.json();\n\n  \u002F\u002F Save to your database\n  for (const job of newJobs) {\n    await db.jobs.upsert({\n      where: { externalId: job.id },\n      create: {\n        externalId: job.id,\n        title: job.title,\n        companyName: job.companies[0]?.name,\n        remoteType: job.remoteType,\n        postedAt: new Date(job.postedAt)\n      },\n      update: {\n        title: job.title,\n        status: job.status\n      }\n    });\n  }\n\n  return newJobs.length;\n}\n\n\u002F\u002F Run daily - fetch jobs posted in the last 24 hours\nconst oneDayAgo = Date.now() - (24 * 60 * 60 * 1000);\nconst newJobCount = await syncNewJobs(oneDayAgo);\n",[477,2239,2240,2258,2274,2293,2306,2315,2319,2333,2348,2352,2381,2385,2392,2396,2430,2434,2439,2460,2481,2504,2513,2528,2543,2572,2587,2610,2614,2623,2637,2651,2655,2663,2667,2671,2683,2687,2691,2696,2742],{"__ignoreMap":475},[480,2241,2242,2244,2246,2249,2251,2254,2256],{"class":482,"line":167},[480,2243,1806],{"class":494},[480,2245,1809],{"class":494},[480,2247,2248],{"class":1585}," syncNewJobs",[480,2250,1589],{"class":485},[480,2252,2253],{"class":1817},"lastSyncTimestamp",[480,2255,1681],{"class":485},[480,2257,851],{"class":485},[480,2259,2260,2262,2264,2266,2268,2270,2272],{"class":482,"line":157},[480,2261,1831],{"class":494},[480,2263,1896],{"class":1290},[480,2265,1821],{"class":485},[480,2267,1582],{"class":485},[480,2269,1586],{"class":1585},[480,2271,1589],{"class":1596},[480,2273,486],{"class":485},[480,2275,2276,2279,2281,2284,2286,2289,2291],{"class":482,"line":194},[480,2277,2278],{"class":1596},"    postedAfter",[480,2280,501],{"class":485},[480,2282,2283],{"class":1290}," lastSyncTimestamp",[480,2285,1757],{"class":485},[480,2287,2288],{"class":1585},"toString",[480,2290,2068],{"class":1596},[480,2292,534],{"class":485},[480,2294,2295,2298,2300,2302,2304],{"class":482,"line":328},[480,2296,2297],{"class":1596},"    limit",[480,2299,501],{"class":485},[480,2301,1281],{"class":485},[480,2303,1927],{"class":528},[480,2305,1304],{"class":485},[480,2307,2308,2311,2313],{"class":482,"line":537},[480,2309,2310],{"class":485},"  }",[480,2312,1681],{"class":1596},[480,2314,1684],{"class":485},[480,2316,2317],{"class":482,"line":279},[480,2318,1690],{"emptyLinePlaceholder":1689},[480,2320,2321,2323,2325,2327,2329,2331],{"class":482,"line":184},[480,2322,1831],{"class":494},[480,2324,1973],{"class":1290},[480,2326,1821],{"class":485},[480,2328,1703],{"class":1702},[480,2330,1706],{"class":1585},[480,2332,1709],{"class":1596},[480,2334,2335,2338,2340,2342,2344,2346],{"class":482,"line":211},[480,2336,2337],{"class":485},"    `",[480,2339,1717],{"class":528},[480,2341,1720],{"class":485},[480,2343,1723],{"class":1290},[480,2345,1726],{"class":485},[480,2347,534],{"class":485},[480,2349,2350],{"class":482,"line":311},[480,2351,509],{"class":485},[480,2353,2354,2357,2359,2361,2363,2365,2367,2369,2371,2373,2375,2377,2379],{"class":482,"line":613},[480,2355,2356],{"class":1596},"      headers",[480,2358,501],{"class":485},[480,2360,1743],{"class":485},[480,2362,1281],{"class":485},[480,2364,1310],{"class":1596},[480,2366,1287],{"class":485},[480,2368,501],{"class":485},[480,2370,1754],{"class":1290},[480,2372,1757],{"class":485},[480,2374,1760],{"class":1290},[480,2376,1757],{"class":485},[480,2378,2029],{"class":1290},[480,2380,2032],{"class":485},[480,2382,2383],{"class":482,"line":634},[480,2384,1098],{"class":485},[480,2386,2387,2390],{"class":482,"line":654},[480,2388,2389],{"class":1596},"  )",[480,2391,1684],{"class":485},[480,2393,2394],{"class":482,"line":660},[480,2395,1690],{"emptyLinePlaceholder":1689},[480,2397,2398,2400,2402,2404,2406,2409,2411,2414,2416,2418,2420,2422,2424,2426,2428],{"class":482,"line":666},[480,2399,1831],{"class":494},[480,2401,1743],{"class":485},[480,2403,2055],{"class":1596},[480,2405,501],{"class":485},[480,2407,2408],{"class":1290}," newJobs",[480,2410,970],{"class":485},[480,2412,2413],{"class":1290}," meta",[480,2415,1950],{"class":485},[480,2417,1821],{"class":485},[480,2419,1703],{"class":1702},[480,2421,1973],{"class":1290},[480,2423,1757],{"class":485},[480,2425,474],{"class":1585},[480,2427,2068],{"class":1596},[480,2429,1684],{"class":485},[480,2431,2432],{"class":482,"line":680},[480,2433,1690],{"emptyLinePlaceholder":1689},[480,2435,2436],{"class":482,"line":685},[480,2437,2438],{"class":1395},"  \u002F\u002F Save to your database\n",[480,2440,2441,2444,2446,2448,2451,2454,2456,2458],{"class":482,"line":706},[480,2442,2443],{"class":1702},"  for",[480,2445,1881],{"class":1596},[480,2447,1573],{"class":494},[480,2449,2450],{"class":1290}," job",[480,2452,2453],{"class":485}," of",[480,2455,2408],{"class":1290},[480,2457,1886],{"class":1596},[480,2459,486],{"class":485},[480,2461,2462,2465,2468,2470,2472,2474,2477,2479],{"class":482,"line":727},[480,2463,2464],{"class":1702},"    await",[480,2466,2467],{"class":1290}," db",[480,2469,1757],{"class":485},[480,2471,2164],{"class":1290},[480,2473,1757],{"class":485},[480,2475,2476],{"class":1585},"upsert",[480,2478,1589],{"class":1596},[480,2480,486],{"class":485},[480,2482,2483,2486,2488,2490,2493,2495,2497,2499,2501],{"class":482,"line":748},[480,2484,2485],{"class":1596},"      where",[480,2487,501],{"class":485},[480,2489,1743],{"class":485},[480,2491,2492],{"class":1596}," externalId",[480,2494,501],{"class":485},[480,2496,2450],{"class":1290},[480,2498,1757],{"class":485},[480,2500,518],{"class":1290},[480,2502,2503],{"class":485}," },\n",[480,2505,2506,2509,2511],{"class":482,"line":767},[480,2507,2508],{"class":1596},"      create",[480,2510,501],{"class":485},[480,2512,851],{"class":485},[480,2514,2515,2518,2520,2522,2524,2526],{"class":482,"line":772},[480,2516,2517],{"class":1596},"        externalId",[480,2519,501],{"class":485},[480,2521,2450],{"class":1290},[480,2523,1757],{"class":485},[480,2525,518],{"class":1290},[480,2527,534],{"class":485},[480,2529,2530,2533,2535,2537,2539,2541],{"class":482,"line":777},[480,2531,2532],{"class":1596},"        title",[480,2534,501],{"class":485},[480,2536,2450],{"class":1290},[480,2538,1757],{"class":485},[480,2540,542],{"class":1290},[480,2542,534],{"class":485},[480,2544,2545,2548,2550,2552,2554,2556,2559,2562,2565,2568,2570],{"class":482,"line":797},[480,2546,2547],{"class":1596},"        companyName",[480,2549,501],{"class":485},[480,2551,2450],{"class":1290},[480,2553,1757],{"class":485},[480,2555,582],{"class":1290},[480,2557,2558],{"class":1596},"[",[480,2560,2561],{"class":601},"0",[480,2563,2564],{"class":1596},"]",[480,2566,2567],{"class":485},"?.",[480,2569,618],{"class":1290},[480,2571,534],{"class":485},[480,2573,2574,2577,2579,2581,2583,2585],{"class":482,"line":818},[480,2575,2576],{"class":1596},"        remoteType",[480,2578,501],{"class":485},[480,2580,2450],{"class":1290},[480,2582,1757],{"class":485},[480,2584,782],{"class":1290},[480,2586,534],{"class":485},[480,2588,2589,2592,2594,2596,2599,2601,2604,2606,2608],{"class":482,"line":839},[480,2590,2591],{"class":1596},"        postedAt",[480,2593,501],{"class":485},[480,2595,1582],{"class":485},[480,2597,2598],{"class":1585}," Date",[480,2600,1589],{"class":1596},[480,2602,2603],{"class":1290},"job",[480,2605,1757],{"class":485},[480,2607,1064],{"class":1290},[480,2609,1953],{"class":1596},[480,2611,2612],{"class":482,"line":854},[480,2613,932],{"class":485},[480,2615,2616,2619,2621],{"class":482,"line":872},[480,2617,2618],{"class":1596},"      update",[480,2620,501],{"class":485},[480,2622,851],{"class":485},[480,2624,2625,2627,2629,2631,2633,2635],{"class":482,"line":889},[480,2626,2532],{"class":1596},[480,2628,501],{"class":485},[480,2630,2450],{"class":1290},[480,2632,1757],{"class":485},[480,2634,542],{"class":1290},[480,2636,534],{"class":485},[480,2638,2639,2642,2644,2646,2648],{"class":482,"line":910},[480,2640,2641],{"class":1596},"        status",[480,2643,501],{"class":485},[480,2645,2450],{"class":1290},[480,2647,1757],{"class":485},[480,2649,2650],{"class":1290},"status\n",[480,2652,2653],{"class":482,"line":929},[480,2654,2037],{"class":485},[480,2656,2657,2659,2661],{"class":482,"line":935},[480,2658,1958],{"class":485},[480,2660,1681],{"class":1596},[480,2662,1684],{"class":485},[480,2664,2665],{"class":482,"line":949},[480,2666,1210],{"class":485},[480,2668,2669],{"class":482,"line":991},[480,2670,1690],{"emptyLinePlaceholder":1689},[480,2672,2673,2675,2677,2679,2681],{"class":482,"line":1021},[480,2674,2210],{"class":1702},[480,2676,2408],{"class":1290},[480,2678,1757],{"class":485},[480,2680,2169],{"class":1290},[480,2682,1684],{"class":485},[480,2684,2685],{"class":482,"line":1054},[480,2686,1216],{"class":485},[480,2688,2689],{"class":482,"line":1059},[480,2690,1690],{"emptyLinePlaceholder":1689},[480,2692,2693],{"class":482,"line":1076},[480,2694,2695],{"class":1395},"\u002F\u002F Run daily - fetch jobs posted in the last 24 hours\n",[480,2697,2698,2700,2703,2705,2707,2709,2712,2715,2718,2720,2723,2726,2729,2731,2733,2735,2738,2740],{"class":482,"line":1095},[480,2699,1573],{"class":494},[480,2701,2702],{"class":1290}," oneDayAgo ",[480,2704,1579],{"class":485},[480,2706,2598],{"class":1290},[480,2708,1757],{"class":485},[480,2710,2711],{"class":1585},"now",[480,2713,2714],{"class":1290},"() ",[480,2716,2717],{"class":485},"-",[480,2719,1881],{"class":1290},[480,2721,2722],{"class":601},"24",[480,2724,2725],{"class":485}," *",[480,2727,2728],{"class":601}," 60",[480,2730,2725],{"class":485},[480,2732,2728],{"class":601},[480,2734,2725],{"class":485},[480,2736,2737],{"class":601}," 1000",[480,2739,1681],{"class":1290},[480,2741,1684],{"class":485},[480,2743,2744,2746,2749,2751,2753,2755,2758],{"class":482,"line":1101},[480,2745,1573],{"class":494},[480,2747,2748],{"class":1290}," newJobCount ",[480,2750,1579],{"class":485},[480,2752,1703],{"class":1702},[480,2754,2248],{"class":1585},[480,2756,2757],{"class":1290},"(oneDayAgo)",[480,2759,1684],{"class":485},[448,2761,2762],{},"This is the feature that makes Jobven practical for real applications. A daily cron job that syncs only new postings uses minimal API calls and keeps your data fresh.",[462,2764,2766],{"id":2765},"rate-limits-transparent-and-tiered","Rate Limits: Transparent and Tiered",[448,2768,2769],{},"Rate limiting is necessary, but it shouldn't be a mystery. Every response includes headers:",[470,2771,2776],{"className":2772,"code":2774,"language":2775},[2773],"language-text","X-RateLimit-Limit: 10\nX-RateLimit-Remaining: 8\nX-RateLimit-Reset: 1733184000\n","text",[477,2777,2774],{"__ignoreMap":475},[448,2779,2780],{},"And the limits are published clearly:",[2782,2783,2784,2803],"table",{},[2785,2786,2787],"thead",{},[2788,2789,2790,2794,2797,2800],"tr",{},[2791,2792,2793],"th",{},"Tier",[2791,2795,2796],{},"Calls\u002FMonth",[2791,2798,2799],{},"Calls\u002FDay",[2791,2801,2802],{},"Calls\u002FSecond",[2804,2805,2806,2820,2834,2847],"tbody",{},[2788,2807,2808,2812,2815,2817],{},[2809,2810,2811],"td",{},"Free",[2809,2813,2814],{},"750",[2809,2816,1671],{},[2809,2818,2819],{},"1",[2788,2821,2822,2825,2828,2831],{},[2809,2823,2824],{},"Starter",[2809,2826,2827],{},"10,000",[2809,2829,2830],{},"334",[2809,2832,2833],{},"5",[2788,2835,2836,2839,2842,2845],{},[2809,2837,2838],{},"Growth",[2809,2840,2841],{},"30,000",[2809,2843,2844],{},"1,000",[2809,2846,2833],{},[2788,2848,2849,2852,2855,2858],{},[2809,2850,2851],{},"Professional",[2809,2853,2854],{},"100,000",[2809,2856,2857],{},"3,334",[2809,2859,2860],{},"10",[448,2862,2863],{},"The free tier is deliberately generous enough to build something real. I want developers to hit the API, build features, and upgrade when they have actual traffic.",[462,2865,2867],{"id":2866},"error-handling-be-helpful","Error Handling: Be Helpful",[448,2869,2870],{},"When something goes wrong, the error response should explain what happened:",[470,2872,2874],{"className":1565,"code":2873,"language":276,"meta":475,"style":475},"async function fetchJobs(params) {\n  const response = await fetch(\n    `https:\u002F\u002Fapi.jobven.com\u002Fv1\u002Fpublic\u002Fjobs?${params}`,\n    {\n      headers: { 'X-API-Key': process.env.JOBVEN_API_KEY }\n    }\n  );\n\n  if (!response.ok) {\n    if (response.status === 401) {\n      throw new Error('Invalid API key');\n    }\n    if (response.status === 429) {\n      const retryAfter = response.headers.get('Retry-After') || 1;\n      throw new Error(`Rate limit exceeded - retry after ${retryAfter}s`);\n    }\n    throw new Error(`API error: ${response.status}`);\n  }\n\n  return await response.json();\n}\n",[477,2875,2876,2893,2907,2921,2925,2953,2957,2963,2967,2989,3012,3035,3039,3060,3101,3132,3136,3166,3170,3174,3190],{"__ignoreMap":475},[480,2877,2878,2880,2882,2885,2887,2889,2891],{"class":482,"line":167},[480,2879,1806],{"class":494},[480,2881,1809],{"class":494},[480,2883,2884],{"class":1585}," fetchJobs",[480,2886,1589],{"class":485},[480,2888,1723],{"class":1817},[480,2890,1681],{"class":485},[480,2892,851],{"class":485},[480,2894,2895,2897,2899,2901,2903,2905],{"class":482,"line":157},[480,2896,1831],{"class":494},[480,2898,1973],{"class":1290},[480,2900,1821],{"class":485},[480,2902,1703],{"class":1702},[480,2904,1706],{"class":1585},[480,2906,1709],{"class":1596},[480,2908,2909,2911,2913,2915,2917,2919],{"class":482,"line":194},[480,2910,2337],{"class":485},[480,2912,1717],{"class":528},[480,2914,1720],{"class":485},[480,2916,1723],{"class":1290},[480,2918,1726],{"class":485},[480,2920,534],{"class":485},[480,2922,2923],{"class":482,"line":328},[480,2924,509],{"class":485},[480,2926,2927,2929,2931,2933,2935,2937,2939,2941,2943,2945,2947,2949,2951],{"class":482,"line":537},[480,2928,2356],{"class":1596},[480,2930,501],{"class":485},[480,2932,1743],{"class":485},[480,2934,1281],{"class":485},[480,2936,1310],{"class":1596},[480,2938,1287],{"class":485},[480,2940,501],{"class":485},[480,2942,1754],{"class":1290},[480,2944,1757],{"class":485},[480,2946,1760],{"class":1290},[480,2948,1757],{"class":485},[480,2950,2029],{"class":1290},[480,2952,2032],{"class":485},[480,2954,2955],{"class":482,"line":279},[480,2956,1098],{"class":485},[480,2958,2959,2961],{"class":482,"line":184},[480,2960,2389],{"class":1596},[480,2962,1684],{"class":485},[480,2964,2965],{"class":482,"line":211},[480,2966,1690],{"emptyLinePlaceholder":1689},[480,2968,2969,2972,2974,2977,2980,2982,2985,2987],{"class":482,"line":311},[480,2970,2971],{"class":1702},"  if",[480,2973,1881],{"class":1596},[480,2975,2976],{"class":485},"!",[480,2978,2979],{"class":1290},"response",[480,2981,1757],{"class":485},[480,2983,2984],{"class":1290},"ok",[480,2986,1886],{"class":1596},[480,2988,486],{"class":485},[480,2990,2991,2994,2996,2998,3000,3002,3005,3008,3010],{"class":482,"line":613},[480,2992,2993],{"class":1702},"    if",[480,2995,1881],{"class":1596},[480,2997,2979],{"class":1290},[480,2999,1757],{"class":485},[480,3001,1081],{"class":1290},[480,3003,3004],{"class":485}," ===",[480,3006,3007],{"class":601}," 401",[480,3009,1886],{"class":1596},[480,3011,486],{"class":485},[480,3013,3014,3017,3019,3022,3024,3026,3029,3031,3033],{"class":482,"line":634},[480,3015,3016],{"class":1702},"      throw",[480,3018,1582],{"class":485},[480,3020,3021],{"class":1585}," Error",[480,3023,1589],{"class":1596},[480,3025,1287],{"class":485},[480,3027,3028],{"class":528},"Invalid API key",[480,3030,1287],{"class":485},[480,3032,1681],{"class":1596},[480,3034,1684],{"class":485},[480,3036,3037],{"class":482,"line":654},[480,3038,1098],{"class":485},[480,3040,3041,3043,3045,3047,3049,3051,3053,3056,3058],{"class":482,"line":660},[480,3042,2993],{"class":1702},[480,3044,1881],{"class":1596},[480,3046,2979],{"class":1290},[480,3048,1757],{"class":485},[480,3050,1081],{"class":1290},[480,3052,3004],{"class":485},[480,3054,3055],{"class":601}," 429",[480,3057,1886],{"class":1596},[480,3059,486],{"class":485},[480,3061,3062,3065,3068,3070,3072,3074,3077,3079,3082,3084,3086,3089,3091,3093,3096,3099],{"class":482,"line":666},[480,3063,3064],{"class":494},"      const",[480,3066,3067],{"class":1290}," retryAfter",[480,3069,1821],{"class":485},[480,3071,1973],{"class":1290},[480,3073,1757],{"class":485},[480,3075,3076],{"class":1290},"headers",[480,3078,1757],{"class":485},[480,3080,3081],{"class":1585},"get",[480,3083,1589],{"class":1596},[480,3085,1287],{"class":485},[480,3087,3088],{"class":528},"Retry-After",[480,3090,1287],{"class":485},[480,3092,1886],{"class":1596},[480,3094,3095],{"class":485},"||",[480,3097,3098],{"class":601}," 1",[480,3100,1684],{"class":485},[480,3102,3103,3105,3107,3109,3111,3113,3116,3118,3121,3123,3126,3128,3130],{"class":482,"line":680},[480,3104,3016],{"class":1702},[480,3106,1582],{"class":485},[480,3108,3021],{"class":1585},[480,3110,1589],{"class":1596},[480,3112,2156],{"class":485},[480,3114,3115],{"class":528},"Rate limit exceeded - retry after ",[480,3117,1720],{"class":485},[480,3119,3120],{"class":1290},"retryAfter",[480,3122,1678],{"class":485},[480,3124,3125],{"class":528},"s",[480,3127,2156],{"class":485},[480,3129,1681],{"class":1596},[480,3131,1684],{"class":485},[480,3133,3134],{"class":482,"line":685},[480,3135,1098],{"class":485},[480,3137,3138,3141,3143,3145,3147,3149,3152,3154,3156,3158,3160,3162,3164],{"class":482,"line":706},[480,3139,3140],{"class":1702},"    throw",[480,3142,1582],{"class":485},[480,3144,3021],{"class":1585},[480,3146,1589],{"class":1596},[480,3148,2156],{"class":485},[480,3150,3151],{"class":528},"API error: ",[480,3153,1720],{"class":485},[480,3155,2979],{"class":1290},[480,3157,1757],{"class":485},[480,3159,1081],{"class":1290},[480,3161,1726],{"class":485},[480,3163,1681],{"class":1596},[480,3165,1684],{"class":485},[480,3167,3168],{"class":482,"line":727},[480,3169,1210],{"class":485},[480,3171,3172],{"class":482,"line":748},[480,3173,1690],{"emptyLinePlaceholder":1689},[480,3175,3176,3178,3180,3182,3184,3186,3188],{"class":482,"line":767},[480,3177,2210],{"class":1702},[480,3179,1703],{"class":1702},[480,3181,1973],{"class":1290},[480,3183,1757],{"class":485},[480,3185,474],{"class":1585},[480,3187,2068],{"class":1596},[480,3189,1684],{"class":485},[480,3191,3192],{"class":482,"line":772},[480,3193,1216],{"class":485},[448,3195,3196],{},"Standard HTTP status codes (401, 429, etc.) plus descriptive error bodies. No proprietary error code systems to memorize.",[462,3198,3200],{"id":3199},"documentation-examples-in-every-language","Documentation: Examples in Every Language",[448,3202,3203],{},"API documentation without code examples is incomplete. Every endpoint in Jobven's docs includes examples in cURL, JavaScript, and Python at minimum:",[470,3205,3209],{"className":3206,"code":3207,"language":3208,"meta":475,"style":475},"language-python shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import os\nimport requests\n\nresponse = requests.get(\n    'https:\u002F\u002Fapi.jobven.com\u002Fv1\u002Fpublic\u002Fjobs',\n    headers={'X-API-Key': os.environ['JOBVEN_API_KEY']},\n    params={\n        'q': 'backend engineer',\n        'remoteType': 'remote',\n        'skills': 'python,postgresql',\n        'experienceLevel': 'mid',\n        'limit': 25\n    }\n)\n\ndata = response.json()\nprint(f\"Found {data['meta']['total']} matching jobs\")\n","python",[477,3210,3211,3219,3226,3230,3246,3258,3292,3300,3319,3337,3355,3373,3387,3391,3395,3399,3415],{"__ignoreMap":475},[480,3212,3213,3216],{"class":482,"line":167},[480,3214,3215],{"class":1702},"import",[480,3217,3218],{"class":1290}," os\n",[480,3220,3221,3223],{"class":482,"line":157},[480,3222,3215],{"class":1702},[480,3224,3225],{"class":1290}," requests\n",[480,3227,3228],{"class":482,"line":194},[480,3229,1690],{"emptyLinePlaceholder":1689},[480,3231,3232,3235,3237,3240,3242,3244],{"class":482,"line":328},[480,3233,3234],{"class":1290},"response ",[480,3236,1579],{"class":485},[480,3238,3239],{"class":1290}," requests",[480,3241,1757],{"class":485},[480,3243,3081],{"class":1585},[480,3245,1709],{"class":485},[480,3247,3248,3251,3254,3256],{"class":482,"line":537},[480,3249,3250],{"class":485},"    '",[480,3252,3253],{"class":528},"https:\u002F\u002Fapi.jobven.com\u002Fv1\u002Fpublic\u002Fjobs",[480,3255,1287],{"class":485},[480,3257,534],{"class":485},[480,3259,3260,3262,3265,3267,3269,3271,3273,3276,3278,3281,3283,3285,3287,3289],{"class":482,"line":279},[480,3261,1738],{"class":1817},[480,3263,3264],{"class":485},"={",[480,3266,1287],{"class":485},[480,3268,1310],{"class":528},[480,3270,1287],{"class":485},[480,3272,501],{"class":485},[480,3274,3275],{"class":1585}," os",[480,3277,1757],{"class":485},[480,3279,3280],{"class":1596},"environ",[480,3282,2558],{"class":485},[480,3284,1287],{"class":485},[480,3286,2029],{"class":528},[480,3288,1287],{"class":485},[480,3290,3291],{"class":485},"]},\n",[480,3293,3294,3297],{"class":482,"line":184},[480,3295,3296],{"class":1817},"    params",[480,3298,3299],{"class":485},"={\n",[480,3301,3302,3305,3307,3309,3311,3313,3315,3317],{"class":482,"line":211},[480,3303,3304],{"class":485},"        '",[480,3306,1345],{"class":528},[480,3308,1287],{"class":485},[480,3310,501],{"class":485},[480,3312,1281],{"class":485},[480,3314,1604],{"class":528},[480,3316,1287],{"class":485},[480,3318,534],{"class":485},[480,3320,3321,3323,3325,3327,3329,3331,3333,3335],{"class":482,"line":311},[480,3322,3304],{"class":485},[480,3324,782],{"class":528},[480,3326,1287],{"class":485},[480,3328,501],{"class":485},[480,3330,1281],{"class":485},[480,3332,1624],{"class":528},[480,3334,1287],{"class":485},[480,3336,534],{"class":485},[480,3338,3339,3341,3343,3345,3347,3349,3351,3353],{"class":482,"line":613},[480,3340,3304],{"class":485},[480,3342,940],{"class":528},[480,3344,1287],{"class":485},[480,3346,501],{"class":485},[480,3348,1281],{"class":485},[480,3350,1640],{"class":528},[480,3352,1287],{"class":485},[480,3354,534],{"class":485},[480,3356,3357,3359,3361,3363,3365,3367,3369,3371],{"class":482,"line":634},[480,3358,3304],{"class":485},[480,3360,823],{"class":528},[480,3362,1287],{"class":485},[480,3364,501],{"class":485},[480,3366,1281],{"class":485},[480,3368,832],{"class":528},[480,3370,1287],{"class":485},[480,3372,534],{"class":485},[480,3374,3375,3377,3380,3382,3384],{"class":482,"line":654},[480,3376,3304],{"class":485},[480,3378,3379],{"class":528},"limit",[480,3381,1287],{"class":485},[480,3383,501],{"class":485},[480,3385,3386],{"class":601}," 25\n",[480,3388,3389],{"class":482,"line":660},[480,3390,1098],{"class":485},[480,3392,3393],{"class":482,"line":666},[480,3394,1953],{"class":485},[480,3396,3397],{"class":482,"line":680},[480,3398,1690],{"emptyLinePlaceholder":1689},[480,3400,3401,3404,3406,3408,3410,3412],{"class":482,"line":685},[480,3402,3403],{"class":1290},"data ",[480,3405,1579],{"class":485},[480,3407,1973],{"class":1290},[480,3409,1757],{"class":485},[480,3411,474],{"class":1585},[480,3413,3414],{"class":485},"()\n",[480,3416,3417,3420,3422,3425,3428,3431,3433,3435,3437,3439,3441,3444,3446,3448,3450,3452,3454,3457],{"class":482,"line":706},[480,3418,3419],{"class":1585},"print",[480,3421,1589],{"class":485},[480,3423,3424],{"class":494},"f",[480,3426,3427],{"class":528},"\"Found ",[480,3429,3430],{"class":601},"{",[480,3432,495],{"class":1585},[480,3434,2558],{"class":485},[480,3436,1287],{"class":485},[480,3438,1112],{"class":528},[480,3440,1287],{"class":485},[480,3442,3443],{"class":485},"][",[480,3445,1287],{"class":485},[480,3447,1127],{"class":528},[480,3449,1287],{"class":485},[480,3451,2564],{"class":485},[480,3453,1678],{"class":601},[480,3455,3456],{"class":528}," matching jobs\"",[480,3458,1953],{"class":485},[448,3460,3461],{},"Copy-paste examples that actually work. No placeholder variables that need to be figured out.",[462,3463,3465],{"id":3464},"what-id-do-differently","What I'd Do Differently",[448,3467,3468],{},"A few things I've noted for future iterations:",[448,3470,3471,3474],{},[1223,3472,3473],{},"GraphQL option"," - Some developers would prefer fetching only the fields they need. REST is simpler to start, but GraphQL might be worth adding.",[448,3476,3477,3480,3481,3483],{},[1223,3478,3479],{},"Webhook support"," - Instead of polling with ",[477,3482,2233],{},", let developers subscribe to new job events. On the roadmap.",[448,3485,3486,3489],{},[1223,3487,3488],{},"SDK packages"," - Official npm and PyPI packages would reduce boilerplate. Currently deprioritized since the REST API is simple enough.",[462,3491,3493],{"id":3492},"try-it","Try It",[448,3495,3496],{},"The API is live with a free tier. If you're building something with job data, give it a try:",[1313,3498,3499,3506],{},[1316,3500,3501],{},[452,3502,3505],{"href":3503,"rel":3504},"https:\u002F\u002Fjobven.com\u002Fdocs",[456],"Documentation",[1316,3507,3508],{},[452,3509,3512],{"href":3510,"rel":3511},"https:\u002F\u002Fjobven.com\u002Fsignup",[456],"Sign up free",[448,3514,3515],{},"The best feedback comes from developers actually using it. Let me know what works and what doesn't.",[3517,3518,3519],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":475,"searchDepth":157,"depth":157,"links":3521},[3522,3523,3524,3532,3533,3534,3535,3536,3537,3538],{"id":464,"depth":157,"text":465},{"id":1256,"depth":157,"text":1257},{"id":1330,"depth":157,"text":1331,"children":3525},[3526,3527,3528,3529,3530,3531],{"id":1338,"depth":194,"text":1339},{"id":1381,"depth":194,"text":1382},{"id":1433,"depth":194,"text":1434},{"id":940,"depth":194,"text":1472},{"id":1517,"depth":194,"text":1518},{"id":1558,"depth":194,"text":1559},{"id":1780,"depth":157,"text":1781},{"id":2226,"depth":157,"text":2227},{"id":2765,"depth":157,"text":2766},{"id":2866,"depth":157,"text":2867},{"id":3199,"depth":157,"text":3200},{"id":3464,"depth":157,"text":3465},{"id":3492,"depth":157,"text":3493},[3540],"Web Development","2025-12-03","How I designed Jobven's public API with cursor-based pagination, flexible filtering, and incremental sync - decisions that make integration straightforward for developers.","md",null,"\u002Fassets\u002Fimages\u002Fblog\u002Fjobven-api.png",{},{"title":30,"description":3542},{"loc":31},[174,276,366],"vyoUyXRndkvntKkG-YMM4jN3yfQClWclNCDbRECtYPY",{"id":3552,"title":98,"author":3553,"body":3555,"categories":3673,"date":3541,"description":3674,"extension":3543,"head":3544,"image":3675,"meta":3676,"minRead":157,"navigation":1689,"ogImage":3544,"path":99,"robots":3544,"schemaOrg":3544,"seo":3677,"sitemap":3678,"stem":100,"tags":3679,"__hash__":3680},"articles\u002Farticles\u002Fjobven.md",{"name":441,"avatar":3554},{"src":443,"alt":441},{"type":445,"value":3556,"toc":3667},[3557,3567,3571,3574,3577,3581,3584,3610,3614,3617,3643,3645,3648],[448,3558,3559,3562,3563,1757],{},[452,3560,98],{"href":454,"rel":3561},[456]," is a job posting API built from the data infrastructure I created for ",[452,3564,106],{"href":3565,"rel":3566},"https:\u002F\u002Fmaplestack.ca\u002F",[456],[462,3568,3570],{"id":3569},"the-backstory","The Backstory",[448,3572,3573],{},"MapleStack aggregates thousands of Canadian tech jobs by scraping directly from employer career pages. After building and refining that system - handling the scrapers, normalizing data, keeping everything fresh - I realized other developers could use the same infrastructure.",[448,3575,3576],{},"Rather than everyone solving the same hard problem (reliable job data collection), I packaged what I'd built into an API. Jobven gives developers access to the same quality data that powers MapleStack.",[462,3578,3580],{"id":3579},"what-makes-it-different","What Makes It Different",[448,3582,3583],{},"Jobven collects job postings directly from employer career pages - the original source. This means:",[1313,3585,3586,3592,3598,3604],{},[1316,3587,3588,3591],{},[1223,3589,3590],{},"Fresh data"," - Jobs appear as soon as employers post them",[1316,3593,3594,3597],{},[1223,3595,3596],{},"No duplicates"," - Each posting traced to a single source",[1316,3599,3600,3603],{},[1223,3601,3602],{},"Structured metadata"," - Skills, salary, location all normalized",[1316,3605,3606,3609],{},[1223,3607,3608],{},"Verified employers"," - Every job linked to a real company",[462,3611,3613],{"id":3612},"who-its-for","Who It's For",[448,3615,3616],{},"Jobven is built for developers creating:",[1313,3618,3619,3625,3631,3637],{},[1316,3620,3621,3624],{},[1223,3622,3623],{},"Niche job boards"," - Remote-only, industry-specific, regional",[1316,3626,3627,3630],{},[1223,3628,3629],{},"Recruiting tools"," - Match candidates, track hiring trends",[1316,3632,3633,3636],{},[1223,3634,3635],{},"Career platforms"," - Add job recommendations to your product",[1316,3638,3639,3642],{},[1223,3640,3641],{},"Market intelligence"," - Analyze hiring patterns and salary data",[462,3644,3493],{"id":3492},[448,3646,3647],{},"The API has a free tier with 750 calls\u002Fmonth - enough to build and test real features.",[1313,3649,3650,3655,3660],{},[1316,3651,3652],{},[452,3653,3505],{"href":3503,"rel":3654},[456],[1316,3656,3657],{},[452,3658,3512],{"href":3510,"rel":3659},[456],[1316,3661,3662],{},[452,3663,3666],{"href":3664,"rel":3665},"https:\u002F\u002Fjobven.com\u002Fpricing",[456],"Pricing",{"title":475,"searchDepth":157,"depth":157,"links":3668},[3669,3670,3671,3672],{"id":3569,"depth":157,"text":3570},{"id":3579,"depth":157,"text":3580},{"id":3612,"depth":157,"text":3613},{"id":3492,"depth":157,"text":3493},[366],"Jobven is a job posting API that collects listings directly from employer career pages. Fresh, structured job data for developers building job boards and recruiting tools.","\u002Fassets\u002Fimages\u002Fblog\u002Fjobven.png",{},{"title":98,"description":3674},{"loc":99},[366,174,383],"pjrg1NVbMA5WnvsX8huNC-TP8ArJ80jbYcubm7HZez0",[3682,3684,3686],{"id":362,"color":149,"description":363,"extension":151,"meta":3683,"name":365,"slug":366,"stem":367,"__hash__":368},{},{"id":271,"color":272,"description":273,"extension":151,"meta":3685,"name":275,"slug":276,"stem":277,"__hash__":278},{},{"id":379,"color":371,"description":380,"extension":151,"meta":3687,"name":382,"slug":383,"stem":384,"__hash__":385},{},1789240766467]