今日已更新 299 条资讯 | 累计 41519 条内容
关于我们

标签:#api

找到 571 篇相关文章

AI 资讯

Shopify's agent-commerce category filter doesn't filter. We checked 190 stores.

Since 2026 every Shopify storefront answers an agent-commerce endpoint at POST /api/ucp/mcp , advertised at GET /.well-known/ucp . Merchants did not turn it on and it is not in their admin. It speaks the Universal Commerce Protocol over JSON-RPC, and the tool that matters is search_catalog : an AI shopping agent asks a store for its catalogue and gets structured product data back - integer prices in minor units with a currency, variants, SKUs, canonical URLs, and a Shopify taxonomy category per product. Fetch the tool list from any store and search_catalog declares catalog.filters.categories , an array of strings documented as "category filters combined with OR logic", next to catalog.filters.price.{min,max} . So an agent should be able to ask for running shoes and get running shoes. We were about to write a paragraph about what it costs a merchant to leave the category field blank. Then we tried it. Method 200 stores, drawn deterministically from a corpus of 10,099 known Shopify storefronts: sort the hostnames, take every Nth. Reproducible, so nobody has to take "we picked 200 stores" on trust. Run on 2 September 2026. Every store got the same five calls, 10 products requested each time: # Call Filter sent A working filter would 1 Control none return products 2 Impossible category gid://shopify/TaxonomyCategory/zz-99-99-99 return nothing 3 The store's own category a category the control's products carry return at least that product 4 Same, unwrapped the bare id without gid://... the other form an agent would try 5 Price control price.max = 1 return nothing - nothing costs a cent Calls 2 and 3 only mean something together. Call 2 alone cannot distinguish "the filter is ignored" from "the filter rejects everything". Those are opposite findings, and both happen. The query matters more than it looks. Generic words ("gift", "set", "new") surface a catalogue's odd corners rather than its catalogue, and produce numbers that are measured honestly and still wrong. Every que

2026-09-06 原文 →
AI 资讯

Scraping 150k+ Instagram followers reliably: batching, resume-on-error, and enrichment

I run a small AI/automation consultancy in Brazil, and a recent lead-research project needed the full follower list of a public Instagram profile — about 153,000 followers — plus enrichment (bio, public email/phone) to find business accounts worth contacting. The problem Pulling a list that size is never one API call. Instagram reports ~153,628 followers; you get them page by page, and any long-running extraction WILL hit a failed request eventually. If your pipeline can't resume, you start over from zero — which is expensive and slow. What I built The pipeline runs on n8n with Supabase as the datastore: Batched extraction — followers are downloaded in batches of up to 10,000 per cycle, on a schedule, instead of one giant run. Resume on error — every page cursor and count is persisted. When a request fails mid-run (in one run it stopped at 4,782 followers after 96 pages read), the job logs the error, emails me a status report, and picks up from the same point on the next cycle instead of restarting. Enrichment pass — a second workflow walks the stored followers and pulls profile details, flagging commercial accounts and any public email/phone in the bio. Personal/private accounts return no contact data, which the report counts separately. Email reports — each cycle sends me a summary: profile, followers reported vs. downloaded, pages read, batch name, and the exact error if one occurred. For the Instagram data layer I used HikerAPI — I tested a few other options first, and it won on pricing and rate limits for this volume. It handled the pagination fine: the run above made 100+ requests without me managing sessions or proxies myself. Tradeoffs / what didn't go perfectly Long extractions still fail sometimes (timeouts); resume logic is not optional at this scale, whatever API you use. Early days for me on this stack: so far it has worked well, but I'm still collecting more data before I'd call the pipeline battle-tested. I'll know more after a few full 150k-follower

2026-09-06 原文 →
AI 资讯

Frontier LLM prices didn't move for 5 months. In August, they moved three times, and one lab tripled its rate.

On August 1 I published a report whose headline finding was that frontier LLM API prices are structurally sticky . Across 40 daily readings of an equal-weight index of ten flagship models — one per lab — not one lab had ever changed the price of an existing model. Every move in the index had come from a new model replacing an old one. August made that sentence false in three weeks. Here's what moved, why the index still ended the month lower , and what happened 72 hours after the cutoff that dwarfs all of it. The month in one table The index is the equal-weight average of ten flagships' blended price per million tokens (3 parts input to 1 part output, list prices as printed on the vendor's own pricing page). Date What happened Index ($/Mtok) Aug 1 Opening level $4.39 Aug 4 Alibaba's slot: Qwen3.7-Max → Qwen3.8-Max ($3.75 → $3.00 blended) $4.32 Aug 16 DeepSeek V4 Pro repriced : flat $0.435/$0.87 → peak $1.32/$3.96 (+264% blended) $4.46 Aug 21 GPT-5.6 Sol repriced : $5/$30 → $4/$20, labelled promotional (−29%) $4.14 Sep 1 Closing level $4.14 Net for the month: −5.7% . Since the first reading on February 23: −9.4% . Three other flagship handovers happened in August (Muse Spark 1.1 → 1.2, Grok 4.5 → 4.6, GLM-5.2 → 5.3) and moved nothing, because each successor kept its predecessor's list price. That's the pattern I described in August. The two bolded rows are the pattern breaking. Move 1: DeepSeek turned "list price" into a schedule Until 16:00 UTC on August 16, DeepSeek V4 Pro billed a single flat rate: $0.435 in / $0.87 out. Then the pricing page split it in two: Peak (01:00–04:00 and 06:00–10:00 UTC): $1.32 / $3.96 Off-peak (every other hour): exactly half — $0.66 / $1.98 The index tracks the peak rate as the list price. Two reasons. DeepSeek defines off-peak as a discount from peak, not the other way round, so peak is the published number. And a caller who doesn't schedule around the clock needs a ceiling, not a floor. But note that even the off-peak rate ($0.99 ble

2026-09-06 原文 →
AI 资讯

Local Business Lead Scrapers on Apify Compared (September 2026)

Most local business lead scrapers on Apify are Google Maps scrapers with a website-crawling step bolted on. lukaskrivka/google-maps-with-contact-details is the most used (87,957 users, 4.63 stars). flash_scraper/local-business-leads is the outlier: it discovers businesses on OpenStreetMap instead of Google Maps, and includes MX email verification in its $3 per 1,000. Every figure below was read from Apify's public Store API ( GET /v2/store ) on 2026-09-05 — including every user count, so they are all on the same footing. The per-actor endpoint ( GET /v2/acts/<id> ) can read one higher: it gives flash_scraper/local-business-leads 33 rather than 32, and code-node-tools 33 as well. Prices, users and ratings change; the Pricing tab on each actor page is authoritative. Disclosure: I publish flash_scraper/local-business-leads , one of the actors compared here. Its limits are listed in the same detail as everyone else's, including the one that will disqualify it for many buyers. How prices are normalised These actors bill per event, and the events differ in kind, which makes headline prices misleading. Some charge per place found. Some charge separately for the website crawl that actually produces the email. Some charge again to verify that the email is deliverable. The table lists the primary per-result event multiplied by 1,000 at the free-plan rate , then names the add-on events, because a $5 per 1,000 place price with a $100 per 1,000 email-verification add-on is not a $5 tool. Paid Apify plans get tiered discounts on several of these actors, ours included — and on the add-on events the discount can be enormous. lukaskrivka's three $100-per-1,000 add-ons fall to $4.00 (email verification), $7.50 (lead enrichment) and $10.00 (social-profile enrichment) per 1,000 on Bronze, and lower again above it (Store pricing record read 2026-09-05). Our own free-plan-to-Diamond spread is about 30 percent. So if you are on a paid plan, re-read every figure below off the Pricing tab:

2026-09-06 原文 →
AI 资讯

From API to AI Agent: Turning a Laravel Backend Into a Tool-Using System

Your team decides to add an AI agent to your Laravel application. The initial plan seems straightforward: give the LLM access to your existing REST API, let it figure out the endpoints, and watch it automate customer support. Then production happens. The agent calls GET /api/users and pulls 14,000 records into its context window, blowing past the token limit and costing $0.80 for a single turn. It tries to POST to a nested route, guesses the JSON payload wrong, and triggers a validation exception. Worse, it calls the refund endpoint without checking if the current user actually owns the order, because your API relies on middleware that the agent orchestrator bypassed. Building an API for human developers or frontend frameworks is fundamentally different from building an API for an AI agent. Humans read Swagger docs and write deterministic code. Agents read JSON schemas, reason probabilistically, and execute in a loop. If you just expose your Laravel routes to an LLM, you aren't building an agent. You're building a very expensive, highly unpredictable curl client. TL;DR: Turning a Laravel backend into an agent-ready system requires shifting from HTTP-centric controllers to action-centric tools. You must generate strict JSON schemas from PHP attributes, enforce authorization inside the tool boundary, curate outputs to protect the context window, handle failures without breaking the agentic loop, and offload execution to background queues. 📋 Table of Contents 1. Stop Exposing Routes, Start Exposing Actions 2. Generating Tool Schemas from PHP Attributes 3. The Authorization Gap: When Agents Bypass Policies 4. Taming the Context Window with Structured Tool Outputs 5. Surviving the "Infinite Retry" Loop on Flaky Tools 6. Building the Agentic Loop with Laravel Queues 7. Defending Against Tool-Output Prompt Injection 8. Observability: Tracing the Agent's Thought Process The Agent-Ready Backend Checklist 1. Stop Exposing Routes, Start Exposing Actions Scenario: You give an L

2026-09-05 原文 →
AI 资讯

How We Built Perceive: Web Content Extraction for RAG Pipelines

A browser and a language model can look at the same URL and effectively see two different things. A browser sees a rendered interface: navigation, cookie banners, buttons, ads, sidebars, images, scripts, interactive components, and eventually the text a human came to read. A language model sees whatever representation we decide to give it. That distinction matters when the URL is going into a RAG pipeline. Open the developer tools on any major news or documentation site and look at the raw HTML. A typical article page runs between 300KB and 800KB of markup. The article text itself is usually between 2KB and 10KB. The ratio of markup to content is consistently between 10:1 and 40:1 depending on how heavily templated the site is. When you pass raw HTML to a language model, you are passing all of it, and most pipelines treat this as an acceptable default. Perceive is the endpoint we built to fix that. You give it a URL. It returns clean Markdown. This post is about what happens in between and why we made the engineering decisions we did. Why raw HTML is a poor RAG input The token waste is real but it is not the worst problem. Three failure modes compound each other. Token waste . A blog post with 800 words of real content can run to 6,000–12,000 tokens as raw HTML once you include navigation, scripts, inline styles, and layout markup. The same content in Markdown is often 900–1,200 tokens. That is not just a cost issue. It is context window space that cannot go to content. Embedding contamination . Embedding models are trained predominantly on natural language. When you embed a chunk containing <div class="sidebar-widget__title">Related Articles</div> alongside the article content, the vector is pulled toward the markup semantics rather than the content semantics . The embedding does not cleanly represent the article; it represents a mixture of the article and the site's component naming conventions. Retrieval degrades as a result: chunks that should be semantically si

2026-09-05 原文 →
AI 资讯

How to Get YouTube Transcripts as a Developer (4 Methods That Work in 2026)

How to Get YouTube Transcripts as a Developer (4 Methods That Work in 2026) YouTube transcripts unlock a lot: AI video summarizers, searchable course databases, RAG over video libraries, dataset generation for fine-tuning, repurposing videos into articles. But getting transcripts programmatically is full of sharp edges: disabled captions, rate limits, datacenter IP blocks, and YouTube's ever-changing frontend. This guide walks through every practical method with working code. Method 4 is the managed service I run. Skip ahead if you just want the API call. The DIY methods below are real and will serve you well for small jobs. What you're actually fetching YouTube stores captions as timed tracks in two flavors: Manual captions : uploaded by creators, best accuracy Auto-generated captions : YouTube's speech recognition, most videos Each track is text plus timing ( text/start/duration ), servable as SRT, VTT, or YouTube's timedtext XML. Everything below ultimately resolves to that shape. Method 1: youtube-transcript-api (Python) The standard open-source library. Start here for scripts and prototypes. pip install youtube-transcript-api from youtube_transcript_api import YouTubeTranscriptApi video_id = " dQw4w9WgXcQ " # the ID from the watch URL transcript = YouTubeTranscriptApi . get_transcript ( video_id ) for entry in transcript : print ( f " [ { entry [ ' start ' ] : . 2 f } s] { entry [ ' text ' ] } " ) It returns a list of dicts, one {'text', 'start', 'duration'} per segment. For other languages, list what's available first, then fetch or translate: tl = YouTubeTranscriptApi . list_transcripts ( video_id ) for t in tl : print ( t . language_code , " generated: " , t . is_generated ) transcript = YouTubeTranscriptApi . get_transcript ( video_id , languages = [ " id " , " en " ]) track = tl . find_transcript ([ " en " ]) translated = track . translate ( " id " ). fetch () # free, server-side Handle the caption-less case explicitly instead of catching bare Exception .

2026-09-05 原文 →
AI 资讯

I built a link shortener with FastAPI and htmx (no JS framework) — the parts that were actually hard

"A URL shortener" sounds like a weekend project. Slug in, long URL out, 302 , done. That's what I thought too. Then real usage showed up: links opened inside Instagram's in-app browser and didn't convert, bot traffic wrecked the analytics, and one link needed to send a US visitor somewhere different from an EU visitor. Suddenly the "trivial" part was 5% of the work. I built the whole thing on FastAPI + Redis + MySQL + htmx , deliberately with no frontend framework . This post is about the parts that turned out to be interesting — the redirect hot path, geo/device routing, and escaping in-app browsers — and why htmx was the right call for a one-person team. Disclosure: I build tapurl.io , a link shortener for marketers. This is a write-up of the engineering behind it, not a pitch — everything below is patterns you can apply to any shortener. The redirect is a hot path, so treat it like one Every other page in the app can be a bit slow. The redirect cannot. It sits in front of someone's click, and it runs on every click, so it has to be a tight, predictable read. The naive version hits your database for every redirect: @app.get ( " /{slug} " ) async def redirect ( slug : str ): link = await db . fetch_link ( slug ) # DB round-trip on every click if not link : raise HTTPException ( 404 ) return RedirectResponse ( link . destination , status_code = 302 ) That's fine until you have traffic. The slug-to-link lookup is a near-perfect cache candidate — a slug maps to the same link record every time. So the real path reads from Redis first and only falls back to MySQL on a miss: async def resolve ( slug : str ) -> Link | None : cached = await redis . get ( f " link: { slug } " ) if cached : return Link . parse_raw ( cached ) link = await db . fetch_link ( slug ) if link : await redis . set ( f " link: { slug } " , link . json (), ex = 3600 ) return link Two things worth saying out loud: Cache the lookup, not the decision. You cache the link record, but the actual destination

2026-09-05 原文 →
AI 资讯

GPT-6 أسترا للمطورين: API، الأسعار، سياق 1M، والفروقات مع GPT-5.6 سول

GPT-6 Astra API: دليل عملي للتسعير والترحيل من GPT-5.6 Sol أصدرت OpenAI نموذج GPT-6 Astra في 3 سبتمبر 2026، أولًا لمجموعة محدودة من المؤسسات، ثم تدريجيًا لمستخدمي ChatGPT Plus وPro وBusiness وEnterprise، وواجهة برمجة تطبيقات OpenAI، وMicrosoft Azure، وAWS Bedrock. معرف النموذج هو gpt-6-astra ، مع نافذة سياق تبلغ 1,050,000 رمز، وتصفه OpenAI بأنه «أفضل نموذج لهندسة البرمجيات حتى الآن». وهو أول نموذج تصنفه OpenAI بأنه حرج لقدرته على الأمن السيبراني، ما يؤثر في سلوكه عبر واجهة برمجة التطبيقات. جرّب Apidog اليوم هذا الدليل يركز على واجهة برمجة التطبيقات: معرف النموذج ونقاط النهاية، أول طلب، مستويات جهد الاستدلال، التسعير، تغييرات الترحيل من GPT-5.6 Sol، وطريقة اختبار ما إذا كانت الزيادة في السعر تستحقها. الخلاصة السريعة معرف النموذج هو gpt-6-astra ، وهو متاح عبر إكمال الدردشة، والردود، والدفعات. لا يدعم الوقت الحقيقي أو المساعدين أو الضبط الدقيق. نافذة السياق: 1,050,000 رمز. الحد الأقصى للإخراج: 128,000 رمز. تاريخ قطع المعرفة: 30 أبريل 2026. يدعم إدخال النص والصورة، ويُخرج النص. التسعير القياسي لكل مليون رمز: 10 دولارات للإدخال، و1 دولار للإدخال المخزن مؤقتًا، و12.50 دولارًا لكتابة ذاكرة التخزين المؤقت، و50 دولارًا للإخراج. تتضاعف أسعار السياق الطويل عند تجاوز 272,000 رمز إدخال. الدفعات وFlex بنصف السعر، بينما الوضع السريع بضعف السعر. مستويات جهد الاستدلال: low و medium و high و xhigh و max . لم يعد none أو minimal متاحًا. أزالت OpenAI معاملات temperature و top_p و logprobs . كما استُبدل prompt_cache_retention بـ prompt_cache_options.ttl . يبقى GPT-5.6 Sol متاحًا بالسعر الترويجي 4 دولارات للإدخال و20 دولارًا للإخراج حتى 21 نوفمبر 2026 على الأقل؛ لذلك يكلف Astra 2.5 ضعف هذا السعر. GPT-6 Astra في لمحة العنصر القيمة معرف النموذج gpt-6-astra نافذة السياق 1,050,000 رمز أقصى إخراج 128,000 رمز تاريخ قطع المعرفة 30 أبريل 2026 الأنماط الإدخال: نص، صورة. الإخراج: نص نقاط النهاية إكمال الدردشة، الردود، الدفعات غير مدعوم الوقت الحقيقي، المساعدون، الضبط الدقيق الميزات البث، المخرجات المهيكلة، استدعاء الدوال، البحث في الملفات، البحث في الويب، التخزين المؤقت للمطالبات، إدخال الصور الأد

2026-09-05 原文 →
AI 资讯

Migrating a Headless CMS? Your Frontend Shouldn't Know About It

A headless CMS migration often sounds simple: Contentful → Strapi Move the content, update the API calls, fix a few components, and you're done. Except... you're usually not. The hardest part of a headless CMS migration isn't moving the content. It's managing the contract between the CMS and the frontend . And if your React or Next.js application is tightly coupled to the CMS response structure, changing the CMS can turn into a much bigger project than expected. The problem Imagine your frontend directly consumes Contentful responses: const ProductCard = ({ product }) => { return ( < article > < h2 > { product . fields . title } < /h2 > < p > { product . fields . description } < /p > < img src = { product . fields . image . fields . file . url } / > < /article > ); }; It works. Until you migrate to Strapi. Now the response might look completely different: product . title product . description product . image . url Suddenly, the frontend needs to understand both CMS structures. And this problem isn't limited to simple fields. Things become much more complicated with: Rich text Media and assets References Nested relations Localization Draft/preview content SEO metadata Dynamic components Pagination GraphQL vs REST Different content modeling approaches The architecture I prefer Instead of allowing React components to consume the CMS directly, introduce a layer between the CMS and the application. ┌───────────────┐ │ Strapi │ └───────┬───────┘ │ ▼ ┌───────────────┐ │ CMS Adapter │ └───────┬───────┘ │ ▼ ┌───────────────┐ │ Domain Model │ └───────┬───────┘ │ ▼ ┌───────────────┐ │ React / Next │ └───────────────┘ The frontend doesn't need to know whether the data came from Strapi, Contentful, Shopify, WordPress, or something else. It just receives the data it needs. For example: type Product = { id : string ; title : string ; description : string ; image : { url : string ; alt : string ; }; }; The CMS adapter is responsible for transforming the CMS response into this model

2026-09-04 原文 →
AI 资讯

eBay's Browse API Doesn't Return Sold Listings. Here Is a Node.js Alternative

If you are building a pricing, resale, or inventory tool, active listings answer the wrong question. The price a seller asks for an item is not necessarily the price a buyer paid. eBay's public Browse API returns active inventory. Marketplace Insights covers sold history, but access is restricted. That leaves many developers maintaining search-page parsers or using a sold-data provider. This example uses CompSniper because it returns completed listings and a price summary through one GET request. Disclosure: I am Marc, the owner of CompSniper. Make one sold-listings request Node.js 20 and newer already include fetch , URLSearchParams , and request timeouts, so this example does not need an HTTP package. const params = new URLSearchParams ({ keyword : " sony wh-1000xm5 " , count : " 10 " , ebaySite : " ebay.com " , itemCondition : " used " , }); const response = await fetch ( `https://api.compsniper.com/v1/scrape? ${ params } ` , { headers : { Authorization : `Bearer ${ process . env . COMPSNIPER_API_KEY } ` , }, signal : AbortSignal . timeout ( 75 _000 ), }, ); const data = await response . json (); if ( ! response . ok ) { throw new Error ( data . error ?? `HTTP ${ response . status } ` ); } console . log ( " Listings: " , data . totalItems ); console . log ( " Median: " , data . summary . median , data . summary . currency ); console . log ( " Range: " , data . summary . p25 , " to " , data . summary . p75 ); for ( const item of data . items . slice ( 0 , 5 )) { console . log ( item . title , item . soldPrice , item . endedAt ); } Keep the API key in a server, worker, or serverless function. Do not place it in browser JavaScript. Select the buyer's marketplace The marketplace matters. A UK reseller normally wants UK sold listings and prices in pounds, not US listings in dollars. const params = new URLSearchParams ({ keyword : " iphone 15 pro -case -charger " , ebaySite : " ebay.co.uk " , count : " 100 " , itemCondition : " used " , minPrice : " 250 " , maxPrice :

2026-09-04 原文 →
AI 资讯

Playwright Test Data: Seeding a Real Backend for E2E Suites

Playwright test data is the set of database rows or API records your application needs to already contain before a browser test runs against it — a logged-in user, their orders, the products those orders reference — generated deterministically so the same run produces the same data every time. Unlike unit tests, a Playwright (or Cypress) spec drives a real browser against a real, running app, which means the backend behind it needs real rows to serve, not an intercepted network response. Getting that data right, and getting it there before the first test starts, is most of what makes a browser E2E suite fast and non-flaky instead of slow and order-dependent. Why is E2E test data hard to manage? Three patterns keep showing up, and each causes a different failure mode: Tests create their own data through the UI. A test that needs an order to exist first signs up a user, logs in, adds a product to a cart, and checks out — all before the actual assertion it cares about. That's slow multiplied across every spec that needs similar setup, and it means the thing under test (the UI) is also the thing doing the setup, so a bug in signup breaks fifty unrelated tests. A shared, mutable test database. If every spec reads and writes the same rows, test order starts to matter: a test that deletes a user breaks a later test that assumed that user still exists. This is one of the most common sources of a suite that passes locally, one file at a time, and fails intermittently in CI when specs run in parallel or in a different order. Hand-maintained fixture SQL or JSON. A fixtures.sql file or a static users.json works until the schema changes — a column gets renamed, a new required field is added — and the fixture silently stops matching what the app expects, or starts failing inserts with no clear signal about which of forty rows is the problem. The fix for all three is the same shape: generate the data the suite needs from a definition (a template), with a fixed seed, right before t

2026-09-03 原文 →
AI 资讯

JsonFabrica vs. Mockaroo vs. Faker.js for Test Data Generation

If you're generating test data today, you've probably landed on one of three approaches: click through a UI like Mockaroo, pull in a library like Faker.js and write generation code yourself, or call a hosted API like JsonFabrica. Comparing these test data generation tools side by side, the real differences aren't about which one produces "better" fake data — Faker.js, Mockaroo, and JsonFabrica are all capable of that. The differences are about where the tool lives, how it handles relationships between records, and who's responsible for running it. Three test data generation tools compared, shape by shape Mockaroo is a browser-based UI: you define columns and types through a web form, preview rows, and export a file — or hit its API directly, which is available even on the free tier (paid tiers raise the volume ceiling rather than gate API access itself). Faker.js is a JavaScript library: you import it into your own code and call functions like faker.person.fullName() or faker.internet.email() to build up objects yourself, one field at a time. JsonFabrica is an API-first hosted service: you send a schema (or use a template) to an endpoint and get structured, schema-conformant JSON back, with no UI step and no library to install in your own codebase. That distinction matters more than it sounds. A UI tool is something a person operates by hand. A library is something a developer owns and maintains inside their own project — you write the loops, the relationships, the edge cases. An API-first tool is infrastructure: something your CI pipeline, your seed script, or an AI coding agent can call directly, without a human in the loop or generation logic living in your repo. UI vs. library vs. API, in practice Mockaroo's UI is genuinely fast for a one-off task — sketch a schema, click generate, download a CSV or JSON file. What it isn't built for is wiring generation into an automated pipeline where nobody is clicking anything. Its API can cover that, but at free-tier volume

2026-09-03 原文 →
AI 资讯

Why API-First Wins for Test Data Generation

Plenty of test data tools are built as a UI first and an API second, if there's an API at all. You open a dashboard, configure some fields, click "generate," and download a file. That works fine for a one-off demo. It falls apart the moment test data generation needs to be part of your actual engineering workflow — running in CI, seeding a database on every branch, or producing ten thousand records instead of ten. That's the case for a test data generation API over a click-driven dashboard: the primary interface is a request you can make from code, and everything else — a UI, a CLI — is built on top of that same API. Automation and CI integration A UI is something a person operates. CI doesn't have a person sitting at it. If test data generation only exists behind a login screen and a click, it can't run as a step in your pipeline — someone has to generate the data ahead of time, commit it, and hope it doesn't drift from what the tests actually need. An API-first tool is just another HTTP call your pipeline makes: fetch fresh, schema-conformant data as part of the build, every run, with no manual step in between. Scriptability — no clicking required Generating test data through a UI means clicking through the same sequence of dropdowns and fields every time you need a new batch. That's tedious for one dataset and untenable for the dozens of shapes a real test suite needs — different entity types, different edge cases, different volumes. An API call is a script. Write it once, parametrize it, and reuse it for every collection you need, without a human repeating the same clicks. Wiring a test data generation API into pipelines and seed scripts Seed scripts are code that runs at a specific point in a workflow — before a test suite, on container startup, in a migration. They need a function call or an HTTP request they can invoke programmatically, not a browser tab. With a test data generation API, "seed the dev database with realistic orders" is a line in a setup scrip

2026-09-03 原文 →
AI 资讯

Baseline – a production FastAPI starter kit

What a "production-ready" FastAPI starter actually needs Every FastAPI project I've started begins the same way: an hour of boilerplate before I write a single line of actual logic. Auth. A database session dependency. A folder structure that won't fall apart once there's more than one resource. A test setup that doesn't take longer to configure than the tests themselves. I got tired of rebuilding it, so I built it once, properly, and wrote down why each piece is shaped the way it is. The structure Every resource in the project follows the same four layers: Router — HTTP in/out only. Parses the request, calls a service, serializes the response. No business logic lives here. Service — business rules. Ownership checks, "does this already exist" decisions, orchestration. No FastAPI imports — this layer doesn't know it's running inside a web framework. Repository — persistence only. SELECT/INSERT/UPDATE/DELETE via SQLAlchemy. No business rules. Schema — Pydantic models for request/response shapes, kept separate from the ORM models. This feels like overkill for a single resource. It stops feeling that way the first time you need the same ownership check enforced in two different routes, or the first time you want to unit-test a business rule without spinning up the whole ASGI app to do it. The decisions that actually mattered Testing against real Postgres, not SQLite. A SQLite-backed test suite gives you false confidence — native UUID types, enum handling, and constraint behavior all differ enough that "tests pass" stops meaning "the Postgres-specific code works." Each test runs inside a SAVEPOINT that gets rolled back afterward, so isolation doesn't cost a schema rebuild per test. Two token types, not one. Short-lived access tokens (15 min) plus longer-lived refresh tokens (30 days), with the token's type claim checked on every decode — a refresh token presented where an access token is expected gets rejected on that alone, not just on signature validity. One error shap

2026-09-03 原文 →
开发者

Warum ich jede fremde Schnittstelle behandle, als würde sie mich verraten wollen

Die meisten schwerwiegenden Vorfälle, die ich erlebt habe, kamen nicht aus dem eigenen Code. Sie kamen von der Grenze, an der mein System mit einem fremden gesprochen hat. Eine API, die plötzlich langsamer antwortete. Ein Feld, das eines Tages null war, obwohl es das nie sein durfte. Eine Antwort, die kein JSON mehr war, sondern eine HTML-Fehlerseite eines Proxys dazwischen. Aus diesen Erfahrungen ist eine Grundhaltung geworden, die vielleicht misstrauisch klingt, aber pragmatisch gemeint ist: Ich vertraue keiner Schnittstelle, die nicht mir gehört. Und ehrlich gesagt vertraue ich auch den eigenen nur bedingt. Das heißt nicht, dass ich von schlechter Absicht ausgehe. Es heißt, dass ich davon ausgehe, dass alles, was schiefgehen kann, irgendwann schiefgeht, und dass es genau dann passiert, wenn es am schlechtesten passt. Ganz konkret bedeutet das ein paar unverhandelbare Punkte. Jeder ausgehende Aufruf hat ein Timeout. Immer. Ein Aufruf ohne Timeout ist ein Aufruf, der mein ganzes System zum Stillstand bringen kann, weil ein Thread ewig auf jemanden wartet, der nie antwortet. Jede Antwort wird geprüft, bevor ich ihr glaube. Ich lese nicht einfach ein Feld aus, ich frage erst, ob es da ist und ob es sinnvoll ist. Und für Ausfälle des Gegenübers habe ich einen bewussten Plan, sei es ein Wiederholungsversuch mit Verzögerung, ein Fallback oder ein sauberer Fehler nach oben. Was mir am meisten Ruhe gebracht hat, ist die Idee des Circuit Breakers. Wenn ein fremder Dienst offensichtlich am Boden liegt, hört mein System auf, ihn immer wieder anzurufen. Es wartet, gibt ihm Zeit, sich zu erholen, und schützt sich selbst davor, im Warten zu ertrinken. Ein sturer Retry gegen ein totes System macht die Sache für alle nur schlimmer. Integration ordentlich zu machen heißt nicht, Fehler zu verhindern. Das kann ich nicht, denn die andere Seite gehört mir nicht. Es heißt, dafür zu sorgen, dass der Fehler des anderen nicht automatisch mein Fehler wird. Die Grenze zwischen zwei Systemen

2026-09-03 原文 →
AI 资讯

ATS Integration Architecture: What to Map Before You Sign

Most guides on choosing an applicant tracking system talk about features. This one is about the layer that actually determines whether the platform survives contact with your stack: integrations. If you're the technical person pulled into an ATS evaluation, this is the part your non-technical colleagues will underestimate — and the part that generates the most post-contract pain. Here's how to map it properly. 1. Inventory every data flow, not every tool Don't list “tools.” List directional data flows. For each system, write down what data moves, which direction, and how often: ●HRIS — candidate → employee record handoff on hire (bidirectional ideally) ●Payroll — new-hire data push (one-way, event-triggered) ●Background screening — order + status callback (bidirectional, webhook-driven) ●Calendar (Google/Microsoft) — interview scheduling + availability sync ●Video interview tools — scheduling links out, recordings/scores back ●Job boards / distribution — posting syndication out, application ingestion back The “direction + trigger” framing exposes gaps that a flat tool list hides. 2. Classify each integration by mechanism Not all “integrations” are equal. Push the vendor to tell you which of these each connection actually uses: ●Native pre-built integration — maintained by the vendor, lowest overhead ●Public REST API — you (or middleware) build and maintain it ●iPaaS / middleware (Workato, Merge.dev, etc.) — flexible, adds cost + a dependency ●Flat-file / SFTP batch — a red flag in 2025 for anything real-time ●“On the roadmap” — treat as does not exist A “yes, we integrate with X” that turns out to be a nightly CSV export is a very different thing from a webhook-driven bidirectional sync. 3. Interrogate the API itself If any integration will run through the public API, evaluate it like you'd evaluate any dependency: ●Is it REST/GraphQL, documented, and versioned? ●Rate limits — do they survive a high-volume hiring event? ●Webhooks for state changes, or are you stuck

2026-09-02 原文 →
AI 资讯

I Built an API That AI Agents Pay in USDC — Full x402 Walkthrough (27 Endpoints, Real Transactions)

I built an Express API that AI agents (or humans, or anything with fetch ) can pay per call, in USDC, with no signup and no API key. It's live on Base mainnet with 27 paid endpoints, and I've run real settled transactions against it. This is the technical walkthrough — the code, the protocol, and the things that actually broke — not an "agentic economy" pitch. What x402 is, in 5 lines x402 resurrects the dormant HTTP 402 Payment Required status code as a real payment handshake. A client calls a paid route → the server replies 402 with payment requirements (amount, asset, network) instead of the resource → the client signs a USDC transfer on Base and replays the request with a PAYMENT header → a facilitator (a third party, or Coinbase's CDP service in production) verifies and settles the transfer on-chain → the server serves the response. No account creation, no API key issuance, no OAuth dance — the wallet address is the identity, and payment is the auth. The seller side The server is plain Express. Each endpoint is a file in endpoints/ exporting { path, method, price, handler } ; server.js loads them all, builds the x402 route table, and mounts one middleware: import { paymentMiddleware , x402ResourceServer } from " @x402/express " ; import { ExactEvmScheme } from " @x402/evm/exact/server " ; import { HTTPFacilitatorClient } from " @x402/core/server " ; import { createFacilitatorConfig } from " @coinbase/x402 " ; const facilitatorConfig = config . isMainnet ? createFacilitatorConfig ( config . cdpApiKeyId , config . cdpApiKeySecret ) : { url : config . testnetFacilitatorUrl }; // https://x402.org/facilitator, no key const facilitatorClient = new HTTPFacilitatorClient ( facilitatorConfig ); const resourceServer = new x402ResourceServer ( facilitatorClient ). register ( config . caip2Network , // "eip155:8453" on mainnet new ExactEvmScheme () ); const paidRoutes = {}; for ( const ep of endpoints ) { if ( ep . price == null ) continue ; paidRoutes [ ` ${ ep . method }

2026-09-02 原文 →