Thrive’s Kushner defends involvement in FIFA mess, hires Elon’s go-to lawyer
New York's prestigious-yet-secretive venture firm Thrive Capital finally speaks out about its involvement in the messy drama upending international soccer.
找到 572 篇相关文章
New York's prestigious-yet-secretive venture firm Thrive Capital finally speaks out about its involvement in the messy drama upending international soccer.
The startup wants to do for IT infrastructure what Cursor did for software engineering.
AIR's platform can discover agents running at a company, continuously vets any skills and add-ons they use, and blocks any unwanted behaviour.
What Rebuilding Kafka From Scratch Actually Teaches You There's a gap between using a system and understanding it. Most engineers never close that gap, and honestly, most of the time that's fine. Kafka works. Topics, producers, consumers, pull the levers, ship the data. Done. But then you hit a weird latency spike, or a consumer group stalls in a way that doesn't match the docs, or replication starts behaving like it has feelings. And suddenly "I know the terminology" doesn't cut it anymore. That's exactly why this rebuild post is worth your time. The Abstraction Tax Every framework you use charges you an abstraction tax. The tax isn't the dependency. It's the mental model debt you carry when something goes wrong and you don't know what layer to blame. Kafka's tax is particularly sneaky because its concepts sound simple: topics are channels, partitions are buckets, offsets are counters. You can get productive fast. And then that simplicity starts lying to you. Why does lag spike when throughput looks fine? Why does adding consumers past the partition count do nothing? Why does a rebalance tank your throughput for 30 seconds? These aren't Kafka quirks. They're direct consequences of how the log is actually structured, consequences that become obvious the second you implement it yourself. What the Rebuild Exposes When you write the log append yourself, the offset model stops being abstract. An offset isn't just a cursor, it's a byte position in a segment file. Consumers aren't "reading from a partition," they're replaying a structured log from a known position. Replication isn't a background checkbox, it's a follower explicitly fetching and acknowledging write positions. A few things that tend to click when you go through this kind of exercise: Segment files and retention , Kafka doesn't delete old messages by scanning. It deletes whole segment files once they're past the retention boundary. If you've ever been surprised by how Kafka handles disk, this is why. Why par
Introduction When you're scaling infrastructure or managing network security, proxies become essential tools—but they solve fundamentally different problems. A reverse proxy sits between your users and your backend servers, while a forward proxy sits between your users and the internet. This distinction might sound academic, but it shapes your entire architecture: from load balancing and security posture to compliance requirements and cost structures. Choosing the wrong proxy type can lead to bottlenecks, security vulnerabilities, or unnecessary infrastructure complexity. This article walks you through real-world scenarios, pricing considerations, and decision frameworks to help you deploy the right solution. Forward Proxies: Controlling Outbound Traffic What Forward Proxies Do A forward proxy intercepts requests from your internal network and forwards them to external servers on the internet. From the external server's perspective, the proxy is the client—the real origin of the request is masked or modified. Common use cases include: Employee internet access control : A company deploys a forward proxy so IT can block malicious domains, filter content, and enforce acceptable use policies Data residency compliance : A financial services firm routes all outbound API calls through a forward proxy in a specific geographic region to meet regulatory requirements Web scraping at scale : When extracting data from multiple websites, forward proxies rotate request sources to avoid IP-based blocking DDoS mitigation for outbound traffic : Distributed request aggregation through a forward proxy can reduce fingerprinting risks Pricing and Infrastructure Costs Forward proxies typically charge per: Concurrent connections : Enterprise solutions like Zscaler or Palo Alto Networks start around $5–15 per user/month Data transferred : Cloud-based forward proxies charge $0.05–$0.30 per GB, depending on geography and provider IP rotation : Proxy services offering residential IPs (for non-
A raw sold-listings search is not automatically a usable comp set. Search for a phone and you may also get cases, chargers, broken screens, empty boxes, and nearby models. Search for a camera lens and you may get caps, adapters, or a different focal length. If those rows go directly into a median, the result can describe the search noise instead of the product. I wanted a larger measurement than a single convenient example, so I ran a fixed 100-product study through CompSniper, the sold-price API I own. The goal was not to prove that an automated classifier is always correct. The goal was narrower: Measure what the production relevance cleaner removed and how the product-level median changed on one predeclared sample. The protocol I selected the products before making the first request: 20 smartphones and tablets 20 gaming and computing products 20 cameras and lenses 20 audio and music products 20 collectibles and luxury products Every search used the same settings: Marketplace: ebay.com Sold window: 2026-06-02 through 2026-08-31 Page: 1 Requested rows: 240 Sort: ended recently Condition: any Relevance cleaning: enabled Each relevance-enabled response contained the raw sample count and raw median captured before classification, followed by the cleaned rows and deterministic price summary from the same fetched page. That meant one production request per product, not separate raw and cleaned fetches. All 100 requests succeeded with unique request IDs. The headline results Across the study: 19,220 priced raw rows were parsed 11,942 priced rows remained after cleaning 7,278 rows were classified out The weighted removal rate was 37.87% 34 of 100 product medians changed by at least 10% 15 of 100 changed by at least 25% 11 of 100 changed by at least 50% The direction was not always upward: 73 medians increased 21 medians decreased 6 medians stayed unchanged That is important. The cleaner is not instructed to raise prices. It tries to retain listings for the requested produ
The seed round for the app that provides real-time legal and policy guidance to officers was led by SignalFire and Las Olas VC.
In the previous article, we learned how to secure our APIs using JWT Authentication and protect routes from unauthorized access. Now let's explore another feature used in almost every AI application— file uploads . If you've built applications like ChatGPT, document Q&A systems, resume analyzers, legal contract reviewers, or medical report analyzers, one thing is common across all of them: The user uploads a file. Without file uploads, there is nothing for the AI model to process. If you haven't read the previous article, check it out first to continue the series: Protecting routes with JWT Tokens Why Do We Need File Uploads? Consider some popular AI applications: ChatGPT allows you to upload PDFs and images. Resume analyzers require your resume. Legal AI assistants analyze contracts. Medical AI systems analyze lab reports. RAG applications build knowledge bases from documents. The workflow usually looks like this: User │ ▼ Upload File │ ▼ FastAPI │ ▼ Save / Read File │ ▼ Process using AI FastAPI makes uploading files extremely simple. Installing Required Package FastAPI uses python-multipart to process uploaded files. Install it using: pip install python-multipart Your First File Upload API FastAPI provides two important classes: File UploadFile Let's import them. from fastapi import FastAPI , File , UploadFile app = FastAPI () Creating the Upload Endpoint @app.post ( " /upload " ) def upload_file ( file : UploadFile ): return { " filename " : file . filename } Run the application. Open Swagger UI. Click POST /upload . You'll notice FastAPI automatically provides a file picker. Upload a file. Response: { "filename" : "resume.pdf" } Our API successfully received the uploaded file. Understanding UploadFile You might wonder: Why didn't we simply use a string or bytes? FastAPI provides the UploadFile class because it contains useful information about the uploaded file. Some commonly used attributes are: file . filename Returns: resume.pdf file . content_type Returns: a
A few weeks ago I wrote about why I built Apiarium after OpenRouter solved one problem for me and I still had four more. The comments on that post ended up shaping a good chunk of what I actually built next, so this is the "here's what changed" follow-up. The interesting part isn't really the features. It's where they came from. Almost everything below started with someone telling me something was annoying, confusing, or missing. So instead of adding things because they looked good on a roadmap, I tried to fix the problems people were actually running into. Multiple API keys, not one shared key for everything The biggest ask came directly from someone using Apiarium in production. They wanted to know which app or feature was actually driving usage, without having to share one API key across everything and lose that signal. So now you can create multiple keys per account: 2 on Free 5 on Starter 10 on Pro You can name them, revoke them individually, and every request is tagged with the key that made it. Credits are still shared across the account, the keys are about visibility, not splitting your balance. // key for your production app fetch ( ' https://api.apiarium.dev/llm ' , { headers : { Authorization : ' Bearer sk-prod-... ' }, ... }) // separate key for a side project // same account, same credits fetch ( ' https://api.apiarium.dev/llm ' , { headers : { Authorization : ' Bearer sk-sideproject-... ' }, ... }) You can also see when each key was last used and filter usage by key in the dashboard. That last part was really the reason I built it. A dashboard that answers "where did my credits go?" The old dashboard was basically a number going down. That wasn't particularly useful. The new dashboard is split into Overview, Usage, API Keys, and Billing. There's a proper date range filter with 7d/30d presets or a custom range, and that same range drives the usage chart, breakdowns, and request logs together. You can break usage down by model and endpoint, so you can ac
Publora is in the Zapier app directory now. I didn't do it to tick a box on some distribution list. My job is making our product easy to live with, and if a user has an agent that can wire us in deeper so they don't have to build the plumbing themselves, I'll go make that happen. Zapier is exactly that case: it connects Publora to thousands of other apps, so nobody has to hand-roll the integration. Worth it. I'd just add that "a no-code platform" and "publishing your own app on a no-code platform" turn out to be two very different Zapiers. Prove it works for users who don't exist yet Here's the requirement I reread three times, sure I'd misunderstood. To submit an app for review, every trigger, every action, and every search has to be tested inside a live Zap, turned on, with at least one successful run in the history. You can't delete those Zaps; the reviewer can ask to see them. So the logic goes like this. You want to publish an app so people can start using it. But to publish it, you first have to prove it's already being used. Run every component for real, as if you had the users you're publishing it to attract. The app isn't in the directory yet, and a history of real use already has to exist. You end up standing in for your own users who aren't there yet. You build the Zaps, run each one, make sure every one has a green run, and don't touch them afterward. A routine task you run like a rocket launch The second surprise. My tasks here are the plain ones: schedule a post, publish a post, delete a post. This isn't a satellite launch. It's what our API does a thousand times a day over one line of code. As a Zapier app, each of those ordinary tasks has to be wrapped, configured, and run live on its own. Create Post, Update Post, Delete Post, two triggers, two searches, each with its own test run under the validator's eye. Scheduling a post is something I can describe in one sentence. Here it became a component with a run history. Then the small surprises a "no-cod
I had twenty-two passing tests and two separate reviewers on a piece of code. None of it objected. Then I pointed it at a real API owned by somebody else and it broke on the first live read. The mismatch fit in one sentence: my parser required ISO 8601, the documented API returned Unix seconds. The repair was not one line. It touched five files, 74 lines of parser and 52 lines of tests. The assumption was small; making it safe was not. Here is why nothing caught it, and it is the part worth keeping: My tests used ISO because my code used ISO, so they agreed with each other and never checked reality. The fixtures were written by the person who wrote the parser. They encoded the same assumption. The suite confirmed internal behaviour without ever challenging the ISO assumption, because both halves of it came from one head. Internally consistent is not the same claim as right, and nothing in that suite could tell the difference. Two separate reviewers missed it too. I cannot prove why, and I am not going to invent a reason. What I can show is that the parser and every fixture encoded the same ISO assumption, so none of the artifacts in front of anyone supplied the live contract that contradicted it. The second one was worse Working against a real system made redirect containment matter, so an independent breaker went at it. In Python 3.13 the default redirect handler rebuilds the redirected request from req.headers , dropping only content length and type. My X-API-Key sat in that header set, so the redirected request inherited it. Python has Request.add_unredirected_header() for exactly this, which marks a header as one that will not be added to a redirected request. I was not using it. The breaker reproduced it offline with a sentinel value and a cross-origin Location , and the sentinel crossed. No live FIPSign credential was ever shown to have crossed an origin. The defect was real and unshipped. I did not find it by auditing my own code, and I did not find it myself
Retries are one of those things that look harmless until the first time they duplicate a real business operation. A request times out, so the client retries it. Reasonable. But what if the first request actually reached the server? What if the application already created the order, reserved the stock, sent the message, or called a payment provider — and only the response was lost? From the client's point of view, the request failed. From the application's point of view, it may already be finished. Send the same request again and you can get the worst kind of bug: one that is technically understandable, difficult to reproduce, and very expensive in production. This is the problem that pushed me to build HttpIdempotencyBundle , a small Symfony bundle for explicit HTTP request idempotency. But the interesting part is not the bundle itself. The interesting part is everything that has to be true before we can safely say: "This request is a retry of the same operation, so we should not execute it again." And just as importantly, what we cannot guarantee. A timeout does not mean the operation failed Consider a simple endpoint: #[Route('/orders', methods: ['POST'])] public function createOrder (): JsonResponse { $order = $this -> orderService -> create (); return new JsonResponse ([ 'id' => $order -> getId (), ], 201 ); } Now imagine this sequence: Client -> POST /orders Server -> creates order #742 Server -> sends 201 response Network -> connection dies Client -> sees timeout Client -> retries POST /orders Nothing unusual happened. The client did exactly what clients often do after a timeout. The server did exactly what it was asked to do. And yet, unless we have another mechanism in place, we may now create order #743 as well. The key idea is simple: transport failure and business-operation failure are not the same thing. HTTP cannot always tell the client whether the operation happened. Give the operation an identity A common solution is an Idempotency-Key . The client g
Live API specs for coding agents An agent writing frontend code has to know the backend's API. It has three options. It can read the backend source and work out from scratch what the service already publishes. It can ask you, which promotes you to API documentation. Or it can swallow the entire OpenAPI document in order to use one route out of it. Then it does the same thing again tomorrow, against a stale swagger.json you exported last week. docs-mcpserver takes the spec straight from the running service, caches it, and serves it one operation at a time. The config { "cacheDir" : "./cache" , "libraries" : [ { "name" : "orders-api" , "description" : "Order handling service" , "sources" : [ { "type" : "url" , "origin" : "https://localhost:5001/openapi/v1.json" , "kind" : "schema" , "name" : "orders" } ] } ] } npm install -g docs-mcpserver claude mcp add docs -- docs-mcpserver --config /path/to/dev-docs.json That is the whole setup. One operation, not the whole spec The agent lists the definitions in orders , picks the one it needs, and fetches that. For an OpenAPI document the path operations are exposed as definitions named GET /orders/{id} , so it can also search by keyword. A few hundred tokens for the operation it is writing against, instead of the entire document. That keeps working as the service grows, which a pasted spec does not. The backend does not have to be running Every call is answered from the cached spec, never from the network. The fetch happens on startup and then in the background while you work, so an endpoint you added 20 seconds ago is already visible. Start the backend once, shut it down, and keep building the frontend. The agent still has real routes and real payload shapes. If the service is down, or answers with something that is not a spec, the last known-good copy keeps being served. Code and issues: github.com/jgauffin/dev-docs-mcp . On npm as docs-mcpserver .
Astrology apps are one of India's quietest huge markets — panchang widgets, kundli generators, matrimonial matching, muhurta pickers. Under every one of them sits the same unforgiving requirement: the astronomy has to be exactly right , because your user's grandmother has a printed panchang on her wall and she will check. I spent the last few months building GrahaAPI — 237 REST endpoints across 23 modules of Vedic astrology, Hindi + English in every response. This post isn't a feature tour. It's the four engineering problems I didn't expect, because I think they're interesting even if you never touch astrology. First, 60 seconds of domain: what the computer actually calculates Strip away the mysticism and Vedic astrology is a coordinate system plus 1,500 years of lookup tables: Tithi (the "lunar date"): the Moon-Sun angular separation, divided into 12° slices. 30 per lunar month. Nakshatra : which of 27 equal 13°20′ segments of the ecliptic the Moon occupies. Dasha : a 120-year planetary period cycle, seeded entirely by the Moon's exact position at birth — a birth-time error of minutes shifts period boundaries by months . The whole thing runs on the sidereal zodiac, offset from the tropical zodiac by ~24° (the ayanamsa — we use Lahiri, the Indian government standard). So: an ephemeris gives you planetary longitudes, and everything else is careful classical bookkeeping. Which brings me to the first bug. Bug #1: the thread-local zodiac Our ephemeris core is a C library with Python bindings, and it holds "which zodiac mode are you in" as global state — per thread . FastAPI runs sync endpoints on a threadpool. First request warms up thread A: sidereal mode set, positions correct. Then a request lands on freshly-spawned thread B: mode silently defaults to tropical , every longitude comes back ~24° off, and — because 24° is almost exactly one nakshatra-and-a-bit — the Moon lands in a plausible but wrong nakshatra. Which seeds the dasha. Which means the API happily returne
Um script meu que publicava conteúdo via API do LinkedIn parou de funcionar do nada. Nenhuma linha mudou. O erro voltou assim: { "status" : 426 , "code" : "NONEXISTENT_VERSION" , "message" : "Requested version 20250601 is not active" } Não tinha bug de código nenhum. A versão da API que eu tinha fixado no script simplesmente saiu da janela de suporte. Como uma API versionada por data quebra sozinha A API REST do LinkedIn exige um cabeçalho LinkedIn-Version no formato ano-mês, tipo 202506 . Isso não é um número de versão semântica que você escolhe uma vez e esquece. É uma janela de tempo. Passado um certo período, aquela versão sai de circulação e a API recusa a chamada, mesmo que o resto do payload esteja perfeito. O código nunca mudou entre o dia em que funcionava e o dia em que parou. O relógio do calendário é que mudou. Por que isso engana quem debuga O reflexo natural ao ver um erro de API é revisar o código: campo errado, token expirado, permissão faltando. Nenhuma dessas hipóteses batia. O token era válido, os escopos estavam certos, o payload era idêntico ao de uma chamada que tinha funcionado antes. A mensagem de erro ajudou bastante aqui, porque nomeou exatamente o campo problemático ( Requested version 20250601 ) e o motivo ( is not active ). Sem prestar atenção nela e ir direto pro código, essa causa levaria muito mais tempo pra aparecer. A correção Trocar o valor fixo do cabeçalho pela versão atual, no mesmo formato ano-mês. Nada mais no código precisou mudar. O detalhe que fica de lição: esse valor não é constante de verdade, é uma data disfarçada de configuração. Documentei isso direto no comentário ao lado da constante, pra próxima vez que o mesmo erro aparecer a causa já estar escrita ali, em vez de descoberta de novo do zero. A lição Nem todo bug tem código culpado. Uma integração que depende de versão de API, certificado, ou qualquer coisa com data de validade pode quebrar sem que uma única linha do seu sistema tenha mudado. Antes de caçar bug no p
Google Flights has a date grid with a fare for every departure day, and a "track prices" toggle that emails you when its pick of dates moves. Both are fine for one trip. Neither gives you the table: every day, the fare, the airline and stops behind it, in rows you can sort, keep and put a threshold on. If your dates are flexible and you want the cheapest day to fly as data — or airfare price tracking that runs every morning — you need rows. This is how to get one row per departure day from Google Flights as JSON, with no API key (there is no public Google Flights API), and how to turn it into a flight price alert. 1. One request, one row per day The Flight Price Tracker on Apify takes routes, a first departure day and a window length. For each day it searches Google Flights, keeps that day's cheapest itinerary and ranks the days. A 30-day window on one route is at most 30 fare rows plus a free status row. curl -X POST "https://api.apify.com/v2/acts/kestrel~flight-price-tracker/run-sync-get-dataset-items?token= $APIFY_TOKEN " \ -H "Content-Type: application/json" \ -d '{"routes": ["LIS-LHR"], "departDate": "2026-10-05", "days": 30, "adults": 1, "currency": "USD", "market": "us"}' A fare row: { "type" : "fare" , "route" : "LIS-LHR" , "trip" : "one_way" , "depart_date" : "2026-10-05" , "return_date" : null , "seat" : "economy" , "adults" : 1 , "currency" : "USD" , "price" : 127 , "price_display" : "127 US dollars" , "airline" : "Tap Air Portugal" , "stops" : 0 , "depart_time" : "8:00 PM" , "arrive_time" : "10:55 PM" , "duration" : "2 hr 55 min" , "duration_minutes" : 175 , "layovers" : null , "co2_kg" : 123 , "itineraries_seen" : 12 , "cheapest_in_window" : true , "rank_in_window" : 1 , "google_url" : "https://www.google.com/travel/flights?tfs=..." , "fetched_at" : "2026-08-29T06:25:14+00:00" } cheapest_in_window is true on exactly one day per route; rank_in_window orders the rest. The free status row repeats the headline as cheapest and cheapest_date , with days_searc
Google Hotels already compares every booking site for a hotel and a stay — Booking.com, Expedia, Agoda, Hotels.com and the hotel's own site. It has a "track prices" button too, but it emails you on its own terms, picks the sources, and keeps the history. If you want the numbers — for a trip, a rate parity check, or a price history chart — you need them as rows. This is how to get Google Hotels prices for exact dates as JSON, without a Google API key (there is no public Google Hotels API for reading prices; the official Hotel APIs are feeds for hotels sending prices to Google), and how to turn that into daily hotel price tracking. 1. One request, every booking site's rate The Google Hotels Prices Scraper on Apify takes a place search or a list of hotels, a stay, occupancy and currency, and returns three row types: hotel (lowest nightly rate + stay total), offer (each source's rate, free‑cancellation flag, deep link) and status . You pay per priced row; sold‑out hotels and empty searches are free. curl -X POST "https://api.apify.com/v2/acts/kestrel~google-hotels-prices/run-sync-get-dataset-items?token= $APIFY_TOKEN " \ -H "Content-Type: application/json" \ -d '{"queries": ["hotels in Lisbon"], "checkIn": "2026-10-03", "checkOut": "2026-10-06", "adults": 2, "currency": "USD", "maxHotels": 20}' A hotel row looks like this: { "type" : "hotel" , "name" : "The Central House Lisbon Baixa" , "check_in" : "2026-10-03" , "check_out" : "2026-10-06" , "nights" : 3 , "nightly" : 81.81 , "nightly_display" : "$82" , "total" : 245 , "stars" : 2 , "rating" : 4.3 , "reviews" : 727 , "deal" : "19% less than usual" , "entity_id" : "ChkIg-b2ismUj7M1Gg0vZy8xMWg3MThreGg1EAE" , "google_url" : "https://www.google.com/travel/hotels/entity/ChkI…" } and an offer row (with "includeOffers": true ): { "type" : "offer" , "name" : "Hyatt Regency Lisbon" , "source" : "Booking.com" , "official" : false , "nightly" : 569.35 , "total" : 1708.05 , "free_cancel" : true , "free_cancel_until" : "Oct 1" , "p
An API operation can receive input from several places. Path parameters identify the record. Query parameters filter or paginate the result. Headers carry metadata or authentication. The request body contains structured data for create and update operations. An MCP tool should give the AI client one clear input schema. That is the mapping problem: HTTP API inputs path + query + headers + body become MCP tool input one structured schema the AI client can understand This tutorial walks through that mapping with practical examples. The goal is to make the tool easy for an AI client to call without hiding the real API contract. Example API operation Imagine a project-management API with this endpoint: PATCH /workspaces/{workspace_id}/projects/{project_id}/tasks/{task_id} It updates one task. The API accepts: path parameters for workspace_id , project_id , and task_id ; query parameters such as notify_assignee ; a request body with the fields to update; authentication through a Bearer token header; an optional request header such as Idempotency-Key . A shortened OpenAPI-style version might look like this: paths : /workspaces/{workspace_id}/projects/{project_id}/tasks/{task_id} : patch : operationId : updateTask summary : Update a task description : " Update the title, status, assignee, or due date for one task." parameters : - name : workspace_id in : path required : true schema : type : string - name : project_id in : path required : true schema : type : string - name : task_id in : path required : true schema : type : string - name : notify_assignee in : query required : false schema : type : boolean default : false - name : Idempotency-Key in : header required : false schema : type : string requestBody : required : true content : application/json : schema : type : object properties : title : " " type : string status : type : string enum : [ todo , in_progress , blocked , done ] assignee_id : type : string due_date : type : string format : date minProperties : 1 securi
There’s a funny (and slightly sad) story behind why I built mabims.dev . It started with a date. More specifically, a Hijri date . Once Upon a Time, the Government Website Had the Date For a long time, Indonesia’s Ministry of Religious Affairs (Kemenag) website displayed the current Hijri date. It was convenient. You opened the website, looked at the corner of the page, and there it was: Today: 30 Sha'ban Simple enough. A lot of people, including me, got used to relying on it. Then one day, something weird happened. A post went viral. Someone noticed that the official calendar published by Kemenag said one date , while the date displayed on Kemenag’s own website said the next day . They were off by one day. People started asking: How can the official website and the official calendar disagree with each other? The post spread. People discussed it. And then… The Solution? Just Delete It. I didn't know what exactly happened behind the scenes. Maybe it was a bug. Maybe it was a calculation issue. Maybe the website was using a different data source. I don't know. But I do remember what happened eventually. The Hijri date disappeared from the website. Problem solved. Technically. If you can't display the wrong date, you can't display a wrong date. Elegant. 😂 At the time, I just thought it was funny. A few years later, I became a developer. And suddenly, the story made a lot more sense. Years Later, I Became a Junior Developer Once I started working as a developer, I learned how easy it is to add a Hijri date to a website. You don't need to calculate the lunar calendar yourself. You just install a library. Or call an API. There are plenty of them. The problem is that most of the libraries and APIs you'll find use Umm al-Qura by default. And that's perfectly reasonable. Umm al-Qura is the official calendar of Saudi Arabia. It's well documented, widely supported, and easy to integrate. For a developer who just wants: Gregorian date → Hijri date it works great. But there's a
Previously I have a macOS App I use myself, gemini-live-translate-macos . It uses ScreenCaptureKit to directly capture audio from a specified App, eliminating the need for virtual sound cards like BlackHole. It then sends the audio to the Gemini Live API for real-time translation, outputting Traditional Chinese subtitles while playing Chinese audio. I've written two posts about the development process: the first one was about building it from scratch using AGY CLI, and the second one was about using Claude Code to take it from "functional" to "user-friendly." The starting point for this new addition was simple: I saw a document for "Real-time Transcription" added to the Live API. Since I was already connected to the Live API, I thought adding a pure transcription mode would just be a matter of changing a few parameters. However, after checking the documentation, I realized that Google released two models with very similar names but very different capabilities at once. The specific feature I actually wanted (speaker diarization) wasn't available at all on the model I originally thought it was. Two Models with Names Differing by Only Two Words Let's lay out the differences first; this is the part I spent the most time figuring out: gemini-3.5-transcribe-live gemini-3.5-transcribe API Used Live API (WebSocket streaming) Interactions API (Standard HTTP request) Usage Scenario Transcribe while speaking Upload the whole file after recording Speaker Diarization Not supported Up to 8 speakers Word-level Timestamps Not supported Supported Audio Length 10 minutes per session 1 hour (30 mins with diarization) Smart Mode SMART available smart is mutually exclusive with diarization Interim Subtitles Has interimInputTranscription Not applicable The official documentation on the Live page's limitations section is very blunt: Speaker diarization is not supported in live streaming sessions. For speaker diarization, use the non-streaming Audio transcription endpoint. So, "seeing who