<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Malachi Stark</title>
    <description>The latest articles on DEV Community by Malachi Stark (@malachi_stark_).</description>
    <link>https://dev.to/malachi_stark_</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3654484%2F1cc463c0-f9fe-41b3-ae42-761d22e7464c.jpeg</url>
      <title>DEV Community: Malachi Stark</title>
      <link>https://dev.to/malachi_stark_</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/malachi_stark_"/>
    <language>en</language>
    <item>
      <title>I built a QR Code API and here's what I learned</title>
      <dc:creator>Malachi Stark</dc:creator>
      <pubDate>Wed, 17 Dec 2025 20:19:35 +0000</pubDate>
      <link>https://dev.to/malachi_stark_/i-built-a-qr-code-api-and-heres-what-i-learned-3l5g</link>
      <guid>https://dev.to/malachi_stark_/i-built-a-qr-code-api-and-heres-what-i-learned-3l5g</guid>
      <description>&lt;p&gt;Last month I needed to generate QR codes server-side for a client project. I tried a few existing APIs and... wasn't thrilled. One charged $49/mo for basic features. Another had a 100-request limit before demanding a credit card. Most had SDKs that felt like they were written in 2012.&lt;/p&gt;

&lt;p&gt;So I built my own. And then productized it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The API
&lt;/h2&gt;

&lt;p&gt;Dead simple. One endpoint:&lt;/p&gt;

&lt;p&gt;curl "&lt;a href="https://api.qrcodeapi.io/generate?data=https://dev.to&amp;amp;size=300%22Returns"&gt;https://api.qrcodeapi.io/generate?data=https://dev.to&amp;amp;size=300"Returns&lt;/a&gt; a QR code. That's it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Options:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;format&lt;/code&gt; - png, svg, or base64&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;size&lt;/code&gt; - 100-1000px&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;color&lt;/code&gt; - hex color for the QR code&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bgColor&lt;/code&gt; - hex color for background&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;errorCorrection&lt;/code&gt; - L, M, Q, or H&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Purple QR code on transparent background
&lt;/h1&gt;

&lt;p&gt;curl "&lt;a href="https://api.qrcodeapi.io/generate?data=hello&amp;amp;color=8b5cf6&amp;amp;bgColor=transparent&amp;amp;format=svg%22##" rel="noopener noreferrer"&gt;https://api.qrcodeapi.io/generate?data=hello&amp;amp;color=8b5cf6&amp;amp;bgColor=transparent&amp;amp;format=svg"##&lt;/a&gt; The interesting parts&lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamic QR Codes
&lt;/h3&gt;

&lt;p&gt;This was the feature I actually needed. Print a QR code on 10,000 flyers, realize the URL has a typo? With static QR codes, you're reprinting.&lt;/p&gt;

&lt;p&gt;With dynamic QR codes, the QR points to a redirect URL that &lt;em&gt;you control&lt;/em&gt;. Change the destination anytime without reprinting anything.&lt;/p&gt;

&lt;p&gt;// Create a dynamic link&lt;br&gt;
const response = await fetch('&lt;a href="https://api.qrcodeapi.io/links" rel="noopener noreferrer"&gt;https://api.qrcodeapi.io/links&lt;/a&gt;', {&lt;br&gt;
  method: 'POST',&lt;br&gt;
  headers: { 'X-API-Key': 'your-key' },&lt;br&gt;
  body: JSON.stringify({ url: '&lt;a href="https://example.com/campaign-v1" rel="noopener noreferrer"&gt;https://example.com/campaign-v1&lt;/a&gt;' })&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;// Returns a short code like "abc123"&lt;br&gt;
// QR code points to: &lt;a href="https://qr.qrcodeapi.io/abc123" rel="noopener noreferrer"&gt;https://qr.qrcodeapi.io/abc123&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;// Later, update the destination:&lt;br&gt;
await fetch('&lt;a href="https://api.qrcodeapi.io/links/abc123" rel="noopener noreferrer"&gt;https://api.qrcodeapi.io/links/abc123&lt;/a&gt;', {&lt;br&gt;
  method: 'PUT',&lt;br&gt;
  headers: { 'X-API-Key': 'your-key' },&lt;br&gt;
  body: JSON.stringify({ url: '&lt;a href="https://example.com/campaign-v2" rel="noopener noreferrer"&gt;https://example.com/campaign-v2&lt;/a&gt;' })&lt;br&gt;
});### Scan Analytics&lt;/p&gt;

&lt;p&gt;Every scan through a dynamic link gets tracked:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Device type (mobile/tablet/desktop)&lt;/li&gt;
&lt;li&gt;Country &amp;amp; city (via IP geolocation)&lt;/li&gt;
&lt;li&gt;Referrer&lt;/li&gt;
&lt;li&gt;Timestamp&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No cookies, no fingerprinting - just basic HTTP request data. Privacy-respecting analytics.&lt;/p&gt;

&lt;p&gt;const stats = await fetch('&lt;a href="https://api.qrcodeapi.io/analytics/abc123" rel="noopener noreferrer"&gt;https://api.qrcodeapi.io/analytics/abc123&lt;/a&gt;', {&lt;br&gt;
  headers: { 'X-API-Key': 'your-key' }&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;// Returns:&lt;br&gt;
// {&lt;br&gt;
//   totalScans: 1247,&lt;br&gt;
//   uniqueScans: 892,&lt;br&gt;
//   byDevice: { mobile: 743, desktop: 401, tablet: 103 },&lt;br&gt;
//   byCountry: { US: 521, UK: 234, DE: 189, ... },&lt;br&gt;
//   byDay: [{ date: '2025-12-15', scans: 87 }, ...]&lt;br&gt;
// }## Tech Stack&lt;/p&gt;

&lt;p&gt;For the curious:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; Vercel Serverless Functions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database:&lt;/strong&gt; Supabase (PostgreSQL)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;QR Generation:&lt;/strong&gt; &lt;code&gt;qrcode&lt;/code&gt; npm package&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Image Processing:&lt;/strong&gt; &lt;code&gt;sharp&lt;/code&gt; for logo overlays&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Geo-IP:&lt;/strong&gt; &lt;code&gt;geoip-lite&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payments:&lt;/strong&gt; Stripe&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The whole thing is ~15 API endpoints consolidated into 9 serverless functions (Vercel Hobby plan has a 12-function limit 😅).&lt;/p&gt;

&lt;h2&gt;
  
  
  TypeScript SDK
&lt;/h2&gt;

&lt;p&gt;Also published an npm package:&lt;/p&gt;

&lt;p&gt;npm install qrcode-api-sdk&lt;br&gt;
import { QRCodeAPI } from 'qrcode-api-sdk';&lt;/p&gt;

&lt;p&gt;const client = new QRCodeAPI({ apiKey: 'your-key' });&lt;/p&gt;

&lt;p&gt;// Generate QR code&lt;br&gt;
const qr = await client.generate({&lt;br&gt;
  data: '&lt;a href="https://dev.to"&gt;https://dev.to&lt;/a&gt;',&lt;br&gt;
  format: 'svg',&lt;br&gt;
  color: '#000000'&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;// Create dynamic link&lt;br&gt;
const link = await client.links.create({&lt;br&gt;
  url: '&lt;a href="https://example.com" rel="noopener noreferrer"&gt;https://example.com&lt;/a&gt;'&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;// Get analytics&lt;br&gt;
const stats = await client.analytics.get(link.shortCode);Full TypeScript types included.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Free:&lt;/strong&gt; 100 QR/month (no credit card)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Starter:&lt;/strong&gt; $9/mo - 5,000 QR/month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pro:&lt;/strong&gt; $29/mo - 50,000 QR/month + analytics + dynamic links&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I tried to price it where indie devs can actually afford it. The free tier is enough for most side projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd do differently
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Start with dynamic QR codes first&lt;/strong&gt; - That's what people actually pay for. Static QR generation is basically a commodity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Build the SDK earlier&lt;/strong&gt; - Having a proper TypeScript SDK with types makes the DX so much better. Should've done this from day one.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Don't underestimate SEO&lt;/strong&gt; - Half my traffic comes from people googling "QR code API Node.js" or "dynamic QR code API". I spent a weekend building SEO landing pages and it was worth it.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Try it out
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🌐 &lt;a href="https://www.qrcodeapi.io" rel="noopener noreferrer"&gt;qrcodeapi.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📚 &lt;a href="https://www.qrcodeapi.io/docs" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📦 &lt;a href="https://www.npmjs.com/package/qrcode-api-sdk" rel="noopener noreferrer"&gt;npm package&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Would love feedback, especially on pricing and what features you'd want to see next. Thinking about adding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bulk generation endpoint (ZIP file with multiple QR codes)&lt;/li&gt;
&lt;li&gt;QR code templates/styles&lt;/li&gt;
&lt;li&gt;Webhook notifications for scan events&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let me know what would be useful! 🙏&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>api</category>
      <category>javascript</category>
      <category>showdev</category>
    </item>
    <item>
      <title>How to Auto-Generate Open Graph Images for Your Blog (No Design Skills Needed)</title>
      <dc:creator>Malachi Stark</dc:creator>
      <pubDate>Wed, 10 Dec 2025 01:39:38 +0000</pubDate>
      <link>https://dev.to/malachi_stark_/how-to-auto-generate-open-graph-images-for-your-blog-no-design-skills-needed-2lca</link>
      <guid>https://dev.to/malachi_stark_/how-to-auto-generate-open-graph-images-for-your-blog-no-design-skills-needed-2lca</guid>
      <description>&lt;h2&gt;
  
  
  🚀 UPDATE: We're Live on Product Hunt!
&lt;/h2&gt;

&lt;p&gt;OG Image API just launched on Product Hunt today! If you found this article helpful, I'd really appreciate an upvote:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://www.producthunt.com/products/og-image-api?launch=og-image-api" rel="noopener noreferrer"&gt;Vote for OG Image API on Product Hunt&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;You know those preview cards that show up when you share a link on Twitter or LinkedIn? Those are Open Graph images, and they can make or break your click-through rate.&lt;/p&gt;

&lt;p&gt;But making them manually in Figma for every blog post? Pain.&lt;/p&gt;

&lt;p&gt;I built an API to solve this. Here's how to use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Every blog post needs an OG image. Without one, your shared links look like this:&lt;/p&gt;

&lt;p&gt;❌ Plain text, no preview, low engagement&lt;/p&gt;

&lt;p&gt;With a good OG image:&lt;/p&gt;

&lt;p&gt;✅ Eye-catching, professional, higher CTR&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Generate Them Programmatically
&lt;/h2&gt;

&lt;p&gt;Instead of designing each image, just call an API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -X POST https://ogimageapi.io/api/generate \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your_key" \
  -d '{
    "title": "How to Build a SaaS in 2025",
    "subtitle": "A complete guide",
    "template": "blog",
    "theme": "dark"
  }' \
  --output og-image.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. One request, one image.&lt;/p&gt;

&lt;h2&gt;
  
  
  In Next.js
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// pages/blog/[slug].js
export async function getStaticProps({ params }) {
  const post = await getPost(params.slug);

  const ogImageUrl = `https://ogimageapi.io/api/generate?title=${encodeURIComponent(post.title)}&amp;amp;template=blog&amp;amp;theme=dark`;

  return {
    props: { post, ogImageUrl }
  };
}

export default function BlogPost({ post, ogImageUrl }) {
  return (
    &amp;lt;&amp;gt;
      &amp;lt;Head&amp;gt;
        &amp;lt;meta property="og:image" content={ogImageUrl} /&amp;gt;
      &amp;lt;/Head&amp;gt;
      {/* ... */}
    &amp;lt;/&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Available Templates
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;blog&lt;/strong&gt; — Perfect for articles&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;product&lt;/strong&gt; — E-commerce products&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;profile&lt;/strong&gt; — Author pages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;stats&lt;/strong&gt; — Metrics/dashboards&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;event&lt;/strong&gt; — Conferences/meetups&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Free Tier
&lt;/h2&gt;

&lt;p&gt;25 images/month free — enough for most personal blogs.&lt;/p&gt;

&lt;p&gt;Check it out: &lt;a href="https://ogimageapi.io" rel="noopener noreferrer"&gt;ogimageapi.io&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;What do you use for OG images? Would love to hear other approaches!&lt;/p&gt;




&lt;p&gt;🚀 &lt;strong&gt;Launching on Product Hunt tomorrow!&lt;/strong&gt; &lt;br&gt;
&lt;a href="https://www.producthunt.com/products/og-image-api" rel="noopener noreferrer"&gt;Follow OG Image API on Product Hunt&lt;/a&gt; to get notified when we go live!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tutorial</category>
      <category>javascript</category>
      <category>seo</category>
    </item>
  </channel>
</rss>
