<?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: Kayvan Zahiri</title>
    <description>The latest articles on DEV Community by Kayvan Zahiri (@kzahiri).</description>
    <link>https://dev.to/kzahiri</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3824617%2F6defa331-c2ad-433d-9742-0c2a88e89b69.png</url>
      <title>DEV Community: Kayvan Zahiri</title>
      <link>https://dev.to/kzahiri</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kzahiri"/>
    <language>en</language>
    <item>
      <title>Your public CSV looks fine until Excel opens it — charset, BOM, and timezone traps</title>
      <dc:creator>Kayvan Zahiri</dc:creator>
      <pubDate>Wed, 30 Sep 2026 22:34:35 +0000</pubDate>
      <link>https://dev.to/kzahiri/your-public-csv-looks-fine-until-excel-opens-it-charset-bom-and-timezone-traps-3p15</link>
      <guid>https://dev.to/kzahiri/your-public-csv-looks-fine-until-excel-opens-it-charset-bom-and-timezone-traps-3p15</guid>
      <description>&lt;p&gt;Public extracts usually fail in pagination or scope. A quieter killer: the file is “correct” in Python and wrong the moment a buyer opens it.&lt;/p&gt;

&lt;p&gt;Before you hand off a public-source CSV, run these five checks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;UTF-8 with an explicit BOM when Excel is in the loop&lt;/strong&gt; — macOS Numbers / &lt;code&gt;pandas&lt;/code&gt; often hide the problem; Windows Excel silently mangling &lt;code&gt;café&lt;/code&gt; / &lt;code&gt;São&lt;/code&gt; / curly quotes is classic. Prefer &lt;code&gt;utf-8-sig&lt;/code&gt; for buyer-facing CSVs, and say so in &lt;code&gt;schema.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No smart punctuation from HTML&lt;/strong&gt; — scrape text often includes NBSP (&lt;code&gt;\u00a0&lt;/code&gt;), zero-width chars, and fancy dashes. Normalize whitespace and replace NBSP → space before write.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timezone is a column, not a vibe&lt;/strong&gt; — timestamps without an offset become “wrong day” after a buyer’s spreadsheet localizes them. Store ISO-8601 with &lt;code&gt;Z&lt;/code&gt; or an explicit offset (e.g. &lt;code&gt;2026-09-30T15:33:00-07:00&lt;/code&gt;), or split into &lt;code&gt;date_utc&lt;/code&gt; + &lt;code&gt;time_utc&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leading zeros survive as text&lt;/strong&gt; — ZIP codes, FIPS, and external ids. Prefix with a tab or force text quoting so Excel does not turn &lt;code&gt;02139&lt;/code&gt; into &lt;code&gt;2139&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Round-trip sample&lt;/strong&gt; — reopen the shipped file in Excel &lt;em&gt;and&lt;/em&gt; LibreOffice once. Spot-check 10 rows for mojibake, shifted dates, and truncated ids. If either client lies, fix the export — not the buyer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Prefer official JSON/API when it exists; flatten once; document encoding + timezone policy in &lt;code&gt;schema.md&lt;/code&gt; next to null rates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Soft CTA
&lt;/h3&gt;

&lt;p&gt;I ship fixed-scope &lt;strong&gt;OpsPacket Public Data Pull&lt;/strong&gt; packs for &lt;em&gt;public&lt;/em&gt; sources only (≤5k rows, ≤12 fields, robots.txt respected) with CSV + schema. Sample shape + landing:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kayvan-zahiri.github.io/opspacket-public-data-pull/" rel="noopener noreferrer"&gt;https://kayvan-zahiri.github.io/opspacket-public-data-pull/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hard no:&lt;/strong&gt; login-walled SaaS exports, LinkedIn, personal email/phone harvest, ToS-hostile targets. No ranking or traffic promises — just a scoped CSV.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>datascience</category>
      <category>python</category>
      <category>csv</category>
    </item>
    <item>
      <title>Pagination nulls eat your CSV — a 5-check public extract checklist</title>
      <dc:creator>Kayvan Zahiri</dc:creator>
      <pubDate>Wed, 30 Sep 2026 18:57:52 +0000</pubDate>
      <link>https://dev.to/kzahiri/pagination-nulls-eat-your-csv-a-5-check-public-extract-checklist-762</link>
      <guid>https://dev.to/kzahiri/pagination-nulls-eat-your-csv-a-5-check-public-extract-checklist-762</guid>
      <description>&lt;p&gt;One-off public extracts usually die in pagination, not in the parser.&lt;/p&gt;

&lt;p&gt;Before you ship a CSV from a public HTML/JSON source, run this checklist:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Count pages vs rows&lt;/strong&gt; — if the site says ~4,200 items and you got 800, you stopped early (or hit a soft wall).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Null spike after page N&lt;/strong&gt; — plot null rates by page index. A sudden jump usually means the template changed or the bot got a thinner HTML shell.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stable dedup key&lt;/strong&gt; — prefer a public id/slug over fuzzy title+city. Re-runs without a key create silent duplicates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Field cap&lt;/strong&gt; — keep ≤12 columns. Extra “nice to have” fields are where nested JSON becomes garbage strings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;schema.md&lt;/strong&gt; — record source pattern, row count, null rates, and what you dropped. Future-you (or a buyer) will thank you.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Prefer official JSON/API when it exists. Flatten nested fields once and note the drop.&lt;/p&gt;

&lt;h3&gt;
  
  
  Soft offer
&lt;/h3&gt;

&lt;p&gt;I sell a fixed-scope &lt;strong&gt;OpsPacket Public Data Pull&lt;/strong&gt; for &lt;em&gt;public&lt;/em&gt; sources only:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;≤5,000 rows · ≤12 fields · robots.txt respected&lt;/li&gt;
&lt;li&gt;CSV + schema.md&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;$149&lt;/strong&gt; ≤72h · &lt;strong&gt;$199&lt;/strong&gt; rush ≤24h&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Landing + sample CSV: &lt;a href="https://kayvan-zahiri.github.io/opspacket-public-data-pull/" rel="noopener noreferrer"&gt;https://kayvan-zahiri.github.io/opspacket-public-data-pull/&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Checkout: &lt;a href="https://kayvanandre.gumroad.com/l/public-data-pull" rel="noopener noreferrer"&gt;https://kayvanandre.gumroad.com/l/public-data-pull&lt;/a&gt; · rush &lt;a href="https://kayvanandre.gumroad.com/l/public-data-pull-rush" rel="noopener noreferrer"&gt;https://kayvanandre.gumroad.com/l/public-data-pull-rush&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Contact: &lt;a href="mailto:kayvanandre@gmail.com"&gt;kayvanandre@gmail.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hard no:&lt;/strong&gt; login walls, CAPTCHA farms, LinkedIn, personal email/phone harvest, ToS-hostile targets.&lt;/p&gt;

</description>
      <category>datascience</category>
      <category>webdev</category>
      <category>python</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Before you build a scraper: a public-data CSV scope card that keeps one-offs honest</title>
      <dc:creator>Kayvan Zahiri</dc:creator>
      <pubDate>Mon, 28 Sep 2026 17:47:57 +0000</pubDate>
      <link>https://dev.to/kzahiri/before-you-build-a-scraper-a-public-data-csv-scope-card-that-keeps-one-offs-honest-2mh4</link>
      <guid>https://dev.to/kzahiri/before-you-build-a-scraper-a-public-data-csv-scope-card-that-keeps-one-offs-honest-2mh4</guid>
      <description>&lt;p&gt;Most “quick scrapes” fail the same way: unclear fields, no row cap, and silent nulls after pagination.&lt;/p&gt;

&lt;p&gt;Before you open Playwright (or hire someone), write this &lt;strong&gt;scope card&lt;/strong&gt; for any &lt;em&gt;public&lt;/em&gt; source:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;URL pattern&lt;/strong&gt; — exact pages or API endpoints (include pagination / sitemap).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Public-only check&lt;/strong&gt; — no login wall, no CAPTCHA farm, robots.txt / ToS allow automated fetch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fields (≤12)&lt;/strong&gt; — name each column; drop “nice to have” extras.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Row budget&lt;/strong&gt; — e.g. ≤5,000 rows. Caps force honesty about coverage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dedup key&lt;/strong&gt; — what makes two rows the same (&lt;code&gt;id&lt;/code&gt;, &lt;code&gt;url&lt;/code&gt;, …).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delivery&lt;/strong&gt; — CSV plus a one-page &lt;code&gt;schema.md&lt;/code&gt; with row count, null rates, and the source pattern.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Prefer official JSON/API when it exists (&lt;code&gt;?$limit=&lt;/code&gt; on open portals often beats HTML parse). Flatten nested fields once; note what you dropped.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sample shape
&lt;/h3&gt;

&lt;p&gt;I keep a public Open Brewery DB (California) slice as an example CSV + schema on the landing below so buyers (and future-me) can see the output shape before paying.&lt;/p&gt;

&lt;h3&gt;
  
  
  Soft offer
&lt;/h3&gt;

&lt;p&gt;I’m the maker of &lt;strong&gt;OpsPacket Public Data Pull&lt;/strong&gt; — fixed-scope public extracts only:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;≤5,000 rows · ≤12 fields · robots.txt respected&lt;/li&gt;
&lt;li&gt;CSV + schema.md&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;$149&lt;/strong&gt; within 72 hours · &lt;strong&gt;$199&lt;/strong&gt; rush within 24 hours&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Landing + sample: &lt;a href="https://kayvan-zahiri.github.io/opspacket-public-data-pull/" rel="noopener noreferrer"&gt;https://kayvan-zahiri.github.io/opspacket-public-data-pull/&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Checkout: &lt;a href="https://kayvanandre.gumroad.com/l/public-data-pull" rel="noopener noreferrer"&gt;https://kayvanandre.gumroad.com/l/public-data-pull&lt;/a&gt; · rush &lt;a href="https://kayvanandre.gumroad.com/l/public-data-pull-rush" rel="noopener noreferrer"&gt;https://kayvanandre.gumroad.com/l/public-data-pull-rush&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hard no:&lt;/strong&gt; login-walled SaaS exports, LinkedIn, personal email/phone harvest lists, ToS-hostile targets. Paste a public URL + fields and I’ll say fit / out-of-scope in one reply.&lt;/p&gt;

&lt;p&gt;No ranking, traffic, or revenue guarantees — just a scoped CSV.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>datascience</category>
      <category>python</category>
      <category>opensource</category>
    </item>
    <item>
      <title>A homepage SEO punch-list that doesn’t become a three-week rabbit hole</title>
      <dc:creator>Kayvan Zahiri</dc:creator>
      <pubDate>Thu, 24 Sep 2026 21:53:03 +0000</pubDate>
      <link>https://dev.to/kzahiri/a-homepage-seo-punch-list-that-doesnt-become-a-three-week-rabbit-hole-o7</link>
      <guid>https://dev.to/kzahiri/a-homepage-seo-punch-list-that-doesnt-become-a-three-week-rabbit-hole-o7</guid>
      <description>&lt;p&gt;Most SEO audits dump hundreds of warnings. Owners still don’t know what to fix first.&lt;/p&gt;

&lt;p&gt;Before you open Screaming Frog or buy a tool, run this &lt;strong&gt;public-HTML&lt;/strong&gt; pass on the homepage (no CMS login):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Title&lt;/strong&gt; — present? ~50–60 characters? Not “Home”?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Meta description&lt;/strong&gt; — present? Roughly under ~155 characters?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;H1&lt;/strong&gt; — exactly one, matching what the page sells?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Image alts&lt;/strong&gt; — content images missing empty alts?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Basics&lt;/strong&gt; — canonical; accidental noindex; status 200&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Sort into &lt;strong&gt;P0 / P1 / P2&lt;/strong&gt;. Ignore vanity scores until those are clean.&lt;/p&gt;

&lt;p&gt;Self-serve checklist: &lt;a href="https://kayvan-zahiri.github.io/opspacket-seo-snapshot/self-serve-punch-list.md" rel="noopener noreferrer"&gt;https://kayvan-zahiri.github.io/opspacket-seo-snapshot/self-serve-punch-list.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’m the maker of OpsPacket. If you want that pass done for you on a public URL you authorize, there’s a free punch-list form: &lt;a href="https://opspacket.com/#free-sample" rel="noopener noreferrer"&gt;https://opspacket.com/#free-sample&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;Optional paid &lt;strong&gt;$49 Site Health Snapshot&lt;/strong&gt; (up to 15 same-site URLs, status CSV, capped top-5 fixes): &lt;a href="https://kayvanandre.gumroad.com/l/seo-snapshot" rel="noopener noreferrer"&gt;https://kayvanandre.gumroad.com/l/seo-snapshot&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;No ranking, traffic, or revenue guarantees.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>webdev</category>
      <category>marketing</category>
      <category>wordpress</category>
    </item>
    <item>
      <title>75% of resumes get rejected by ATS before a human sees them — here's how to check yours for</title>
      <dc:creator>Kayvan Zahiri</dc:creator>
      <pubDate>Sat, 14 Mar 2026 22:51:07 +0000</pubDate>
      <link>https://dev.to/kzahiri/75-of-resumes-get-rejected-by-ats-before-a-human-sees-them-heres-how-to-check-yours-for-5e1a</link>
      <guid>https://dev.to/kzahiri/75-of-resumes-get-rejected-by-ats-before-a-human-sees-them-heres-how-to-check-yours-for-5e1a</guid>
      <description>&lt;p&gt;Hey everyone, I've been frustrated with how many qualified people get rejected by ATS before a human ever&lt;br&gt;
  sees their resume. So I built a free tool that scores your resume against any job description.&lt;/p&gt;

&lt;p&gt;How it works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Paste your resume text + the job description&lt;/li&gt;
&lt;li&gt;Get a score out of 100 across 5 categories (keyword match, formatting, sections, content quality, length)&lt;/li&gt;
&lt;li&gt;See exactly which keywords you're missing&lt;/li&gt;
&lt;li&gt;Get specific suggestions to improve&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's completely free, no sign-up required: &lt;a href="https://withresumeai.com/ats-checker" rel="noopener noreferrer"&gt;https://withresumeai.com/ats-checker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's also a free AI resume builder if you want to start from scratch:&lt;br&gt;
  &lt;a href="https://withresumeai.com/builder/new" rel="noopener noreferrer"&gt;https://withresumeai.com/builder/new&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Would love feedback on what could make it more useful!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>startup</category>
      <category>saas</category>
    </item>
  </channel>
</rss>
