AI 资讯
I Ran 300K Company API Lookups. 40K Hit Military Bases.
security, #api, #cybersecurity, #discuss On July 30, 2026, my batch job finished 300,000 domain-to-company lookups. 39,847 of them (13.3%) resolved to defense contractors, military-adjacent parent companies, or headquarters within a few miles of named bases. I wasn't hunting for that. I was just trying to clean a CRM. The same day, lina published a post about hijacking e164.arpa zones and accidentally logging hundreds of thousands of phone calls to military bases. Different protocol, same smell: an infrastructure lookup that was supposed to be boring turned into a classified-adjacent data spill. That parallel is what made me sit down and write this. Here is the exact call I used, with the live response for github.com so you can see the shape of the data before I explain what went wrong. import requests , json , time # Full source notes: https://github.com/On13uka/company-info-api RAPIDAPI_KEY = " YOUR_RAPIDAPI_KEY " BASE = " https://company-info1.p.rapidapi.com " def lookup ( domain ): r = requests . get ( f " { BASE } /lookup?domain= { domain } " , headers = { " X-RapidAPI-Key " : RAPIDAPI_KEY , " X-RapidAPI-Host " : " company-info1.p.rapidapi.com " }, timeout = 20 ) return r . json () print ( json . dumps ( lookup ( " github.com " ), indent = 2 )) The response I got back looked like this. It is a cached sample from a real call — the endpoint was asleep when I drafted this, but the fields are exactly what the pipeline consumed. { "domain" : "github.com" , "company_name" : "GitHub Inc" , "wikipedia" : "GitHub is a developer platform..." , "ceo" : "Thomas Dohmke" , "founded" : "2008" , "headquarters" : "San Francisco, California" , "employees" : "3000+" , "parent_company" : "Microsoft" , "twitter" : "@github" , "github_org" : { "repos" : 200 , "stars" : 50000 , "followers" : 12000 }, "health_score" : 78 } The Finding I started the job because a sales team had 300,000 stale domain records and wanted company names, headcounts, and a rough health score for each. The pla
AI 资讯
How to launch an AI automation agency offering voice AI agents for local businesses
You'll build a repeatable service that lets plumbers, dentists, and other service-business owners answer calls with a natural-sounding, AI-driven voice that schedules appointments, qualifies leads, and captures payments. The result is a hands-free phone front-desk that you can sell as a monthly subscription and use to acquire new clients for your agency. What you'll get: a working n8n workflow that wires Anthropic's Claude, ElevenLabs text-to-speech, and Twilio Programmable Voice together, plus a go-to client-acquisition script that turns the service into a scalable AI automation agency. What you need Tool Plan / Price* Role n8n (self-hosted Docker) Free (self-hosted) - see Docker Hub for latest image Orchestrates API calls, stores conversation state Twilio Programmable Voice Pay-as-you-go - check Twilio pricing page Provides inbound phone numbers and SIP bridge Anthropic Claude API Usage-based - check Anthropic pricing page Generates conversational replies ElevenLabs TTS API Usage-based - check ElevenLabs pricing page Turns Claude's text into a lifelike voice Cloudflare DNS + SSL Free tier available - verify limits Publishes a secure webhook for Twilio Git (optional) Free Version-controls workflow JSON *We avoid stating exact free-tier caps; always verify the current provider pricing. Estimated time-to-build: 12-16 hours total (including testing and client-onboarding script). Defining the core pieces Voice AI is the combination of speech-to-text, natural-language generation, and text-to-speech that lets a computer hold a phone conversation. In this guide we skip the speech-to-text step by letting Twilio forward the caller's audio to our n8n webhook; the rest happens via APIs. Key insight: The biggest revenue lever for an AI automation agency is the repeatable client-acquisition funnel, not the underlying technology. Building voice ai agents for local businesses Below is a step-by-step walkthrough. Every step mentions the exact UI field, API endpoint, or n8n node na
AI 资讯
how to build voice ai for inbound calls
You can have a Vapi agent answer every inbound call, ask qualifying questions, and hand the prospect off to Calendly to lock in a meeting - all without writing a single line of custom telephony code. The result is a self-contained voice AI agent that routes calls, captures lead data, and books calendar slots automatically. voice is the audible sound produced by a human speaker that can be captured, transmitted, and synthesized by software. voice AI agent is a software component that receives spoken input over a phone line, runs speech-to-text, applies a language model, and returns synthesized speech to the caller. Below you'll find everything you need to reproduce the exact workflow, from the required services to the n8n JSON that creates the Vapi agent, plus the pitfalls that usually bite new builders. What you need Tool Plan / Price Role Vapi Free tier or paid plan - check the Vapi pricing page Voice AI platform that hosts the conversational model and performs voice synthesis Twilio Pay-as-you-go voice minutes - check Twilio pricing Provides the inbound phone number and SIP termination for Vapi Calendly Free tier or paid plan - check Calendly pricing Calendar link generator and meeting scheduler n8n (self-hosted) Community edition - free (Docker) Orchestrates the webhook chain between Vapi, Twilio, and your CRM HubSpot CRM (optional) Free tier - check HubSpot pricing Stores qualified lead details for follow-up Estimated build time: 1-2 days for a minimal production-ready flow, assuming you already have accounts for the services above. how to build voice ai for inbound calls The core of the solution is a Vapi "agent" that runs a scripted dialogue, a Twilio phone number that forwards calls to Vapi, and an n8n workflow that receives the webhook payload, enriches the lead, and creates a Calendly event. Follow each numbered step precisely; the configuration values are written exactly as they appear in the UI. 1. Provision a Twilio phone number Log into the Twilio Conso
AI 资讯
The best free AI models 2026 for an automation-first business
The best free AI models 2026 are the ones that give you production-grade quality without a bill at the end of the month. In practice that means using Groq's ultra-low-latency mix, Google Gemini's 1 M-token free quota, Meta's LLaMA 2 (self-hosted), DeepSeek's open-source v2.5, and Mistral-7B-Base on a free cloud tier. Hook them up to an automation platform like n8n and you can run a full SaaS pipeline - lead scoring, email drafting, image captioning, or ticket routing - without paying for inference. Below you'll find the exact stack, a step-by-step build, the gotchas that usually bite newcomers, and a short FAQ so you can get the best free AI models 2026 live in under two hours. What you need Tool / Model Plan / Price (as of 2026) Role in the pipeline Groq (Mixtral-8x7B-instruct) Free tier: 200 k tokens / month, no credit-card required (see Groq pricing) Low-latency text generation for chat & summarisation Google Gemini 1.5 Flash Free tier: 1 M input tokens / month, 0.5 M output tokens / month (check Google Cloud AI) Multi-modal (text + image) support, best for classification and translation Meta LLaMA 2 13B Self-hosted Docker (CPU) - $0, or hosted on Runpod free credits (up to $5) Deep-knowledge base Q&A, fine-tuning on proprietary data DeepSeek-V2.5 Free tier on DeepSeek API: 150 k tokens / month (no card) Creative writing, code suggestions Mistral-7B-Base Free tier on Mistral Cloud: 100 k tokens / month (requires OAuth) Structured data extraction, function calling n8n (automation) Community Edition (self-hosted Docker) - free Orchestrates API calls, branching, retries Docker Desktop Free for personal use Container runtime for LLaMA 2 Node.js 18+ Free (runtime) Needed for custom JS functions inside n8n Estimated build time: 90 minutes for a fresh machine (install Docker, pull LLaMA, configure n8n) plus 30 minutes of testing. Total ~2 hours. Building a production-grade automation pipeline with the best free AI models 2026 Below is a concrete example: an inbound-lead
AI 资讯
The duration your video API accepts is not the duration it renders
A sequence I cut to a music bed was three frames out at the first transition, nine at the second, and by the sixth segment nothing lined up with anything. I had asked every generation for ten seconds. Every generation had returned a file that was not ten seconds. Nothing in the API said so. The request took duration: 10 , returned 200 , and produced an MP4 whose container duration was 8.708 . No warning field, no note in the response body, and — the part that actually cost me the afternoon — no mention of it on the docs page I had read three times. This is a general property of latent video models rather than a bug in one provider, and once you know the shape of it you can handle it in about twenty lines. Here is the shape. Seconds are the wrong unit A video diffusion model does not work on frames. It works on a compressed latent tensor, and the compression is temporal as well as spatial: a causal 3D autoencoder folds a run of input frames into a single latent frame. Because the encoder is causal, the first frame is kept whole and everything after it is compressed in groups. With a temporal stride of s , a clip of F frames becomes latent_frames = ( F - 1 ) / s + 1 which only divides evenly when F ≡ 1 (mod s) . Frame counts that miss that condition get padded or truncated, so implementations pick the nearest legal count and render that instead. Stack a second constraint on top — many of these models generate in fixed blocks of latent frames rather than one at a time — and the set of renderable lengths collapses into a short arithmetic progression: F = head + block · n n ∈ ℕ Every legal duration is one of those F values divided by the frame rate. Nothing between them is reachable. duration: 10 is not a request. It is a hint that gets snapped to a grid you were never shown. What the snapping does to users Three separate problems, and only the first is obvious. The output is not the length you promised. Your UI said 10s, the file is 8.708s, so your UI lied. Not by much,
AI 资讯
Why I Built a No-Signup QR & URL Utility Platform (And How to Use the API)
We've all been there: a client needs a quick QR code for a print campaign, or a short link for social media. You search Google, click the first result, and realize you need to create an account, verify your email, and potentially pay after 14 days. To solve this friction, we built klick.tools . It's a collection of simple web tools that just work - no signup, no expiration dates, and full GDPR compliance. What's inside? QR code generator: 7 content types (URL, text, WiFi, vCard, email, phone, geo), custom colors and module shapes, automatic WCAG contrast check, and clean export as PNG, SVG or PDF. Rendering happens in the browser, so the payload never leaves the device. URL shortener: shorten a link in seconds, see click stats, and change the target later without reprinting anything. The developer API We didn't want to build just another consumer site, so everything is backed by a REST API. Base URL: https://klick.tools/api/v1 . Responses are always JSON - lists as { data, count } , writes as { message, data } , errors as { error } with a stable machine-readable code . Creating a short link is a single POST, and it works without an account at all (rate-limited per IP): curl -X POST https://klick.tools/api/v1/links \ -H "Content-Type: application/json" \ -d '{"targetUrl": "https://example.com/a-very-long-campaign-url"}' With an API key ( kt_live_... , generated in your account) the link is bound to you, so you get click counts, editing and higher quotas. Pass it as a Bearer token or via x-api-key : const res = await fetch ( " https://klick.tools/api/v1/links " , { method : " POST " , headers : { " Content-Type " : " application/json " , Authorization : `Bearer ${ process . env . KLICK_TOOLS_API_KEY } ` , }, body : JSON . stringify ({ targetUrl : " https://example.com/landing " , title : " Summer campaign " , }), }); const { data } = await res . json (); console . log ( data . shortUrl , data . clickCount ); The same pattern covers QR codes via /api/v1/qr - create, li
AI 资讯
Iran Doesn't Need to Mine Hormuz — Your requirements.txt Is Already Rigged
Iran Doesn't Need to Mine Hormuz — Your requirements.txt Is Already Rigged Every headline you've read this week is a diversion. The Strait of Hormuz is not the target. You are. And you have been for months, possibly years, while you retweeted tanker tracking maps and debated whether Brent crude would touch $150. Iranian state-sponsored groups — OilRig, APT33, MuddyWater, Agrius — did not spend the last decade pivoting to cloud infrastructure so they could watch you panic about a waterway. They did it so they could own your build pipeline while you were distracted. And they have. This is not speculation. CISA Advisory AA24-038A explicitly maps Iranian APT campaigns against U.S. and allied critical infrastructure to cloud identity, Kubernetes targets, and software supply chains. Not SCADA. Not PLCs. Your kubectl binary. Your Helm charts. That FastAPI microservice running payment webhooks that you deployed on a Friday and haven't touched since March. The Revolutionary Guard does not need a mine. They need a maintainer who hasn't updated python-jose in fourteen months. The Theater and the Operation You watched the Strait. They watched your CI/CD. Geopolitical analysis is a spectator sport for infrastructure engineers, and Iranian cyber command is the bookie. While your LinkedIn feed filled with satellite imagery and retired admirals explained chokepoint logistics, the actual operation ran silently against: Public Helm charts with hardcoded cluster-admin ServiceAccounts FastAPI services with python-multipart handling unbounded file uploads on single-threaded Uvicorn workers .kube/config files exfiltrated from developer laptops in a dev-legacy namespace that predates your current CTO Terraform state stored in a single S3 bucket with versioning disabled and a policy written by someone who left in 2021 The Hormuz closure narrative is Information Operations . The closure of your API gateway due to an unpatched ASGI memory exhaustion vulnerability is the kinetic effect. You a
AI 资讯
5 states, 2 working filters: scraping US childcare license registries
Five states, one query language, and an "active licenses only" checkbox that only actually filters two of them. That's the trap in scraping US childcare-license open-data registries: Socrata SODA makes every state's API look identical, but "active" is defined — or not defined at all — differently in every dataset. Quick answer New York, Connecticut, Colorado, Delaware, and Texas all publish their childcare-facility registries through Socrata, and all five accept the same $where query syntax. But only NY and CT ship a server-side status filter this Actor can apply. Colorado and Delaware have no status column in the dataset at all — there's nothing to filter on. Texas does have a status column ( operation_status ), it's just not wired into the active-only filter, so toggling activeOnly doesn't touch Texas rows either way. Treating "active only" as a global switch that behaves the same everywhere will silently hand you closed and revoked facilities in three of the five states while you believe you filtered them out. STATE_CONFIGS : dict [ str , StateConfig ] = { " NY " : StateConfig (..., col_status = " facility_status " , active_where = " facility_status= ' Active '" ), " CT " : StateConfig (..., col_status = " status " , active_where = " status= ' ACTIVE '" ), " CO " : StateConfig (..., col_status = None ), # no status column to filter on " DE " : StateConfig (..., col_status = None ), # no status column to filter on " TX " : StateConfig (..., col_status = " operation_status " ), # status exists, filter isn't wired } Why does "active only" do nothing in three states? Because the filter is applied per-state, not globally, and only two states have both a status column and a configured $where fragment for it: async def _fetch_page (...): params = { " $limit " : str ( page_limit ), " $offset " : str ( offset ), " $order " : config . order_key } if active_only and config . active_where : params [ " $where " ] = config . active_where return await _get_with_retry ( session
AI 资讯
Same API standard, four incompatible schemas: scraping state cosmetology license registries
"Just query the Socrata API" is true and also useless advice. Socrata SODA is a real open standard — New York, Connecticut, Colorado, and Texas all expose their professional-license registries through the same $limit / $offset / $where query language. The standard ends there. What each state puts inside that standard is four unrelated data models wearing the same protocol. Quick answer Every state's cosmetology/barber/salon registry is one giant multi-profession table with its own column names, its own beauty-credential filter, and its own idea of what "active" means — and one state (Texas) doesn't expose a status column at all, so an activeOnly toggle is a silent no-op there. A generic Socrata client that assumes one schema will either miss most of the data or crash on the first state whose columns don't match. The fix is a per-state config object that maps each state's real column names to one canonical output row, with the active-license filter applied only where the underlying data supports it. @dataclass ( frozen = True ) class StateConfig : state : str endpoint : str order_key : str col_business_name : str | None col_licensee_name : str | None col_status : str | None base_where : str | None = None active_where : str | None = None Why does the same query return different professions per state? Cosmetology licenses don't get their own dataset — they're rows buried inside each state's entire professional-licensing table, next to electricians, dentists, and notaries. Filtering has to happen server-side, in SoQL, before pagination even starts, or you're downloading (and paying to store) irrelevant rows. Texas needs a starts_with() match across three license-type prefixes plus an Establishment wildcard; Connecticut needs an exact in() list of six credential names; Colorado needs a four-code in() list: TX_BEAUTY_WHERE = ( " starts_with(license_type, ' Cosmetology ' ) " " OR starts_with(license_type, ' Class A Barber ' ) " " OR starts_with(license_type, ' Barber ' ) "
AI 资讯
Google Trends API: the 200 OK that means you got soft-blocked
Google Trends has no public API. What it has is the same internal JSON endpoints the trends.google.com single-page app calls — and those endpoints do something most REST clients aren't built to survive: they answer with HTTP 200 and an empty body when Google decides you look like a bot. Quick answer A 200 OK from Google Trends' widgetdata endpoints does not mean you got data. If the response body is empty, Google soft-blocked the request without bothering to send a 429. The fix is to stop trusting the status code alone: check resp.text.strip() on every call that's supposed to return a payload, and if it's empty, rotate the proxy session and retry exactly like you would on a 429 — because that's what it functionally is. if status == 200 : if require_body and not resp . text . strip (): # Soft block: Google returns 200 with empty body when it detects bots. # Treat the same as 429 — rotate session and retry. logger . warning ( " %s: HTTP 200 but empty body (soft block, attempt %d/%d) " , ...) if proxy_cfg is not None : new_sid = _fresh_session_id () current_proxy_url = await proxy_cfg . new_url ( session_id = new_sid ) await asyncio . sleep ( delay ) continue return resp Why does a working response start with )]}' ? Every Trends JSON endpoint prepends an XSSI-protection prefix before the actual JSON body — a defence against cross-site script inclusion attacks that predates fetch() . Naive json.loads(resp.text) throws a JSONDecodeError on a perfectly healthy response. Worse, the prefix isn't even consistent: /trends/api/explore sends )]}'\n (no comma), some widgetdata endpoints send )]}',\n (with a comma). We check the longer variant first so a response using the short prefix doesn't get mis-stripped: XSSI_PREFIX = " )]} ' , \n " XSSI_PREFIX_NO_COMMA = " )]} ' \n " def _strip_xssi_prefix ( body : str ) -> str : if body . startswith ( XSSI_PREFIX ): return body [ len ( XSSI_PREFIX ):] if body . startswith ( XSSI_PREFIX_NO_COMMA ): return body [ len ( XSSI_PREFIX_NO_COMMA
AI 资讯
How to pull every open job from Greenhouse, Lever, Ashby and SmartRecruiters with public APIs (and monitor changes)
Job postings are one of the most underrated public data sources on the internet. Recruiters use them to spot placement opportunities, B2B teams read them as buying signals (a new Head of Data means data-tooling budget), and job seekers want to apply on day one — not when a posting finally reaches the aggregators. The usual instinct is to scrape career pages. Don't. Most tech companies host their careers page on one of a handful of Applicant Tracking Systems (ATS), and the four biggest ones — Greenhouse, Lever, Ashby and SmartRecruiters — all expose public, documented JSON APIs . No auth. No proxies. No brittle HTML selectors. The career page itself loads the same JSON you're about to fetch. In this tutorial we'll build a single-file Python tool that: fetches every open job for a company from any of the four ATS, auto-detects which ATS a company uses, normalizes everything into one clean schema, monitors changes — run it on a schedule and get only new / removed / changed postings. The four endpoints ATS Endpoint Greenhouse GET https://boards-api.greenhouse.io/v1/boards/{slug}/jobs?content=true Lever GET https://api.lever.co/v0/postings/{slug}?mode=json Ashby GET https://api.ashbyhq.com/posting-api/job-board/{slug} SmartRecruiters GET https://api.smartrecruiters.com/v1/companies/{slug}/postings (paginated) The {slug} is the company identifier you see in career-page URLs: boards.greenhouse.io/stripe → stripe , jobs.lever.co/spotify → spotify , jobs.ashbyhq.com/linear → linear , careers.smartrecruiters.com/Visa → Visa . Try one right now — no API key needed: curl -s "https://api.ashbyhq.com/posting-api/job-board/linear" | head -c 400 Step 1 — fetchers, one per ATS Each API returns a different shape, so we normalize as we fetch. Here are all four (Python 3, only requests ): import requests UA = { " User-Agent " : " ats-jobs-tutorial/1.0 " } def get_json ( url , params = None ): r = requests . get ( url , params = params , headers = UA , timeout = 30 ) r . raise_for_statu
AI 资讯
Driving DaVinci Resolve's Free Edition with Claude, From Inside the App
The wall Every MCP server that controls DaVinci Resolve connects to it the same way: a script running outside the app calls into Resolve's scripting API over the network. That works fine on Resolve Studio. On the free edition it doesn't work at all — Lite is sandboxed and blocks any script that isn't launched from inside Resolve itself. The one door left open Free Resolve still runs Python scripts launched from its own Workspace > Scripts menu. A menu script gets the resolve object injected for free, can run a long-lived loop, and — because the sandboxed app ships the com.apple.security.network.server entitlement — can open a localhost listening socket. That's the whole trick: the MCP server is the menu script. Claude Code ──HTTP JSON-RPC (MCP)──▶ 127.0.0.1:8765/mcp │ server runs INSIDE Resolve │ (Workspace > Scripts > Utility) ▼ command queue → main script thread ▼ global `resolve` object → Resolve API What it gets you 157 tools across editing, color, render, media pool, and Fusion title styling — driven from plain-language requests in Claude Code. Zero dependencies: pure Python standard library, so there's nothing to pip install into Resolve's bundled interpreter. Try it git clone https://github.com/2sem/davinci-resolve-lite-mcp.git cd davinci-resolve-lite-mcp ./install.sh macOS only for now. Full tools reference and demo video in the repo.
AI 资讯
Regex Against a PDF: The One Endpoint That Skips OCR Entirely
Most document pipelines have a reflex. A PDF comes in, and the first instinct is: run OCR, then parse it. That reflex costs time and money on documents that never needed it in the first place. Here's the distinction that gets skipped over. A PDF generated from Word, from an invoicing system, from a web page, from almost any modern software, is "born digital." Every character on the page is already stored as text, positioned and selectable, the same way this article's text is selectable in your browser. A scanned PDF is different: it's a photograph of a page, a grid of pixels with no text underneath it at all. OCR exists to solve that second problem. It reads the pixels and reconstructs a text layer that wasn't there. PDF OCR is PDF4me's endpoint for exactly that job, and its own documentation lists "Intelligent Processing: skip OCR when text is already searchable to optimize performance" as a named feature, which is the whole thesis of this article in one line. But if the PDF already has a text layer, running it through OCR first is a wasted step: extra processing time, extra cost, extra room for OCR to introduce recognition errors into text that was already perfect. A large share of the PDFs moving through business automation, generated invoices, exported reports, system-generated confirmations, contracts drafted in Word and exported to PDF, are born digital from the start. They don't need OCR. They need something that can read the text layer that's already there and pull out exactly the values that matter. That's what Extract Text by Expression does. One regex, one endpoint POST https://api.pdf4me.com/api/v2/ExtractTextByExpression No OCR step. No AI model. No template you have to build in a dashboard first. The request is small: Parameter Type Required Description docContent Base64 String Yes The source PDF, Base64-encoded docName String Yes Filename with .pdf extension expression String Yes A standard regular expression: groups, quantifiers, and anchors all supp
AI 资讯
Vibe Coding vs Prompt-Driven: One Year Later, the Debate Is Already Outdated
A year ago, the debate between “vibe coding” and “prompt-driven development” felt like the right lens for using AI in software. Today it’s outdated. The real frontier is no longer how to phrase a request, but how to build an environment that lets an AI agent explore, modify, and verify code safely. The shift from conversational prompts to structured development harnesses changes what it means to ship reliable software. Below are four concrete ways this evolution is reshaping daily work. From Prompt to Harness A finely crafted prompt can still produce a working feature, but it won’t prevent an agent from inventing APIs, skipping tests, or misreading business rules. The new bottleneck is the harness: Git for traceability, tests for feedback, linting for style, and explicit rules for constraints. When these elements are in place, even a short instruction like “fix the bug” can trigger a reliable, auditable workflow. Code is Abundant; Good Software is Rare Generating hundreds of lines of code costs almost nothing, but verifying that those lines align with product history, security policies, and hidden constraints is still manual. The developer’s scarcest resource is now judgment: deciding what to automate, what to document, and when to override the agent’s plausible but wrong solution. Documentation is Now for Machines README files and ADRs used to serve only humans. Today they also feed the agent’s context. A well-maintained AGENTS.md , coherent tests, and executable examples let an AI act with minimal prompting while remaining aligned with the team’s conventions. The repository becomes a self-explaining environment. Supervision is the Next Skill Single-agent workflows are giving way to multi-agent orchestration. Frontend, API, test analysis, and incident triage agents must coordinate without stepping on each other. Observability, conflict detection, and rollback mechanisms replace prompt tuning as the critical layer of control. The center of gravity has moved from “ho
AI 资讯
I Built a Claude Code Skill That Reverse-Engineers Undocumented APIs
I Built a Claude Code Skill That Reverse-Engineers Undocumented APIs Because "the docs are in the code" is not a documentation strategy. The Week I Lost to Grepping I joined a new team last month. Day 1 task: add a feature to the billing service. Day 1 reality: I opened the API docs and realized they were from 2022. Half the routes had been rewritten. The other half never had docs to begin with. So I did what every backend dev does. I grepped. grep -r "app.get|app.post|router." src/ --include="*.js" Four hours later, I had a notebook full of endpoints, a headache, and zero confidence that I had found everything. I found routes that worked but were not documented. I found docs for routes that did not exist anymore. I found one GET /invoices/:id endpoint with zero auth checks that had been sitting there since 2022. This is normal. And it should not be. The Idea What if I could drop a single file into a repo and have Claude Code map the entire API layer for me? Not from annotations. Not from existing OpenAPI specs. From the actual code. So I built it. Meet API Archaeologist API Archaeologist is a Claude Code / Codex CLI skill that reads your source code and reverse-engineers your API layer. It finds: • Internal endpoints — REST, GraphQL, gRPC, WebSockets • External integrations — third-party APIs, webhooks, SDK clients • Auth flows — JWT, OAuth, API keys, session cookies, RBAC • Security gaps — unauthenticated routes, hardcoded secrets, missing rate limits • Dead code — auth middleware with no endpoints, orphaned routes And it generates two things: API_DISCOVERY.md — A complete catalog with Mermaid diagrams openapi-draft.yaml — A draft OpenAPI spec How It Works The skill is just a SKILL.md file. Claude Code reads it and follows the instructions. It: Discovers route definitions Traces handlers, DTOs, middleware, services, and database calls Maps authentication and authorization Finds external API calls and integrations Flags potential security and reliability risks Gene
AI 资讯
GitHub API Rate Limits: an Unauthenticated 304 Still Costs You a Request
No token. One IP. July 29, 2026: GET /repos/python/cpython 200 5996 B remaining 32 -> 31 + If-None-Match (no Authorization header) 304 0 B remaining 31 -> 30 + If-None-Match 304 0 B remaining 30 -> 29 + If-None-Match 304 0 B remaining 29 -> 28 Three conditional requests. Three 304 Not Modified . Zero bytes of body across all three. Three requests gone from a bucket of 60 per hour. I opened the terminal to write the opposite post. The short version: if you call the GitHub REST API without an Authorization header, an If-None-Match request that comes back 304 still decrements x-ratelimit-remaining . The ETag saves you bytes. It does not save you quota. GitHub's documentation states the claim five times on one page and attaches the condition to two of them, and that clause falls off easily when a sentence gets quoted on its own. The post I meant to write My working title was something like "poll GitHub for free with ETags". I believed it. I had read the sentence about 304 responses not using your rate limit, I had repeated it to other people, and the plan was a tidy little piece with a before-and-after budget chart. The first run killed it. remaining went down. My first reaction was that my counter reading was wrong, which is the normal reaction and usually the correct one. It was not wrong. So the post changed, and the finding turned out to be worth more than the one I went in with. Does a 304 count against the GitHub rate limit? What the docs actually say Here is the part that matters, and I want to be precise because it would be easy and dishonest to turn this into "GitHub's docs are wrong". They are not. On the page Best practices for using the REST API the claim shows up five times. Two of the five carry a condition; three do not. Here is the strict one, the only place on the page where the condition is spelled out as a header: "Making a conditional request does not count against your primary rate limit if a 304 response is returned and the request was made while c
AI 资讯
Designing Reliable APIs for Production Applications: Lessons From Building Real-World Digital Products
Designing Reliable APIs for Production Applications: Lessons From Building Real-World Digital Products APIs are often described as the “bridge” between different parts of an application, but building a production-ready API involves much more than sending data from a frontend to a backend. Through my experience building full-stack applications, I've learned that a good API needs to be designed around reliability, security, maintainability and the actual needs of its users. Here are some of the principles I now consider when designing APIs: Design around resources, not screens An API shouldn't simply mirror the frontend interface. It should expose meaningful resources and operations that can evolve independently from the UI. Validate everything at the API boundary Data coming from a client should never be trusted automatically. Request validation, type checking and clear error responses help prevent invalid data from propagating through the system. Authentication is only the beginning An authenticated user should not automatically have access to every resource. APIs need appropriate authorisation and access-control rules for sensitive operations. Design predictable errors A useful API doesn't just return “something went wrong.” Clients need consistent status codes and structured error responses so that applications can respond appropriately. Think about idempotency This becomes particularly important when an API handles operations such as payments, orders or other actions that shouldn't accidentally happen twice because of a network retry. Don't expose unnecessary data APIs should return what the client needs rather than exposing entire database records. This reduces unnecessary data transfer and can also reduce the risk of accidentally exposing sensitive information. Logging and observability matter An API can appear perfect during development and still fail in production. Good logging and monitoring make it possible to understand what happened when requests fail, la
AI 资讯
Building a Production ML Trading Dashboard with the Dhan API
Real integration notes for wiring NIFTY ML models to live broker data via Dhan. Research/ paper-trading context — not a live-trading recommendation. Why Dhan Dhan's API exposes direct option-chain access — exactly what an options-ML system needs: POST /optionchain — full chain for an underlying POST /optionchain/expirylist — available expiries Fields: security_id , last_price , volume , oi , previous_oi , implied_volatility , top_bid_price , top_ask_price , and greeks (delta/theta/gamma/vega) Security IDs are stable: NIFTY = 13 (IDX_I) , BANKNIFTY = 10001 (IDX_I) . The Pipeline Shape A research dashboard pulls live chain + underlying, runs the trained XGBoost model on each new 15-minute bar, and displays: side score (CE/PE alignment) gate state (entry ready / blocked) contract quality scores a doctrine/backtest report Keep the inference path separate from the execution path . The dashboard shows; a permissioned, human-approved module places orders. Paper Trade First The DhanLiveTrader pattern: load the model, predict on each new bar, place long orders with configurable SL/TP (default 1.0 ATR SL, 2.0 ATR TP), and run in paper mode first . Only after stable out-of-sample + paper evidence should any execution module even be considered. { "client_id" : "YOUR_DHAN_CLIENT_ID" , "access_token" : "YOUR_DHAN_ACCESS_TOKEN" , "is_paper_trade" : true , "nifty_symbol" : "NIFTY" , "quantity" : 50 , "max_trades_per_day" : 3 , "sl_atr_mult" : 1.0 , "tp_atr_mult" : 2.0 } The Hard Part: Stops A known footgun: using a Stop-Loss Limit (SL-L) order with price = sl − 0.05 means it won't fill if price crashes through the stop. Prefer SL-Market for the protective stop. Execution quality is its own research topic — don't bolt it on at the end. Honest Status The ML side of this stack showed real directional skill (60.5% top-decile accuracy) but the fixed-SL backtest was still unprofitable (PF 0.53). A dashboard that displays an honest "RESEARCH / PAPER" status is worth more than one that hid
AI 资讯
How to upload a file over JSON-RPC, when JSON has no type for a file
JSON has no representation for a file. Strings, numbers, arrays, objects - that is the whole list. So every JSON-RPC API eventually runs into the same question: how do you accept a file upload - a photo, a scan, a PDF - when the protocol itself cannot carry binary data? The usual answer is: you don't. The file goes to a separate, ordinary controller that reads $request->files , and the JSON-RPC layer handles everything else next to it. And now you have exactly the ad hoc endpoint sprawl that JSON-RPC was supposed to remove. In otezvikentiy/json-rpc-api 5.2 there is a different answer. And more interesting than the feature is how it came about: I did not write it - an external contributor did. But first things first. Full disclosure: I am the author of the bundle, and I have maintained it alone for almost three years. That is exactly why a release whose headline feature was written by someone else feels like a different kind of event to me. The problem Straight from issue #8 : two services exchange scanned images plus structured metadata (tenant, station, session) on the same call - something like captures.create(tenantId, stationId, image) . Today image cannot be expressed as a parameter of a JSON-RPC method, so that call has to live outside the bundle as a separate multipart controller. What you want is for the method to simply declare a parameter of type UploadedFile and get the file, like any other parameter. The solution: multipart as a transport adapter The key idea is to leave the core untouched. A multipart/form-data request is normalized into the very same JSON-RPC envelope an ordinary request produces, only with UploadedFile objects already sitting inside params . Everything below the transport - hydration, batching, validation - stays completely unaware of multipart, exactly the way it is unaware that a GET request's payload came from a query string. The wire format: one text part named jsonrpc carries the full JSON-RPC envelope as a string (all scalar par
AI 资讯
Three Lines to Draw Before You Scrape Instagram
Most write-ups on this subject are about technique. This one is about the three decisions you should make before you write any code, because in my experience every project that went badly went badly for a reason that was decided on day one and not noticed until much later. I have built this kind of collection twice, for competitive analysis and for a partner-vetting workflow. Neither of them needed to touch anything behind a login, and I want to explain why that turned out to be the useful constraint rather than the limiting one. Line one: the login wall is a boundary A login wall is a statement about who the content is for. Treating it as an engineering obstacle to be routed around is the decision that puts a project on the wrong side of everything: terms of service, the platform's own detection, and in several jurisdictions the law. So the first line is simply: if it requires an account to see, it is out of scope. Not "hard," not "for later." Out of scope. I am not going to discuss techniques for getting past one, and I would be sceptical of any article that does. The interesting engineering question here is not how to see more. It is how much you can actually do with what is openly published, and the honest answer is: considerably more than people assume before they check. This constraint also has a practical benefit that is easy to miss. A pipeline built only on openly available data does not break when authentication changes, does not require credential management, and does not put an account at risk. Mine has survived two platform changes that took down colleagues' authenticated collectors. Line two: public does not mean unrestricted The second line is the one developers get wrong most often, and it has nothing to do with access. Data being publicly visible says nothing about whether you may store it, for how long, or what you may do with it. In the EU and UK, information about an identifiable person is personal data whether or not they published it themselves