<?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: zhenye yu</title>
    <description>The latest articles on DEV Community by zhenye yu (@wanfeng).</description>
    <link>https://dev.to/wanfeng</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%2F3991932%2F87b28d27-02a9-4315-9081-30c9521f3bc8.jpg</url>
      <title>DEV Community: zhenye yu</title>
      <link>https://dev.to/wanfeng</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wanfeng"/>
    <language>en</language>
    <item>
      <title>curl</title>
      <dc:creator>zhenye yu</dc:creator>
      <pubDate>Fri, 19 Jun 2026 07:57:30 +0000</pubDate>
      <link>https://dev.to/wanfeng/curl-33m2</link>
      <guid>https://dev.to/wanfeng/curl-33m2</guid>
      <description></description>
    </item>
    <item>
      <title>From Browser cURL to Clean Python Requests Code, Step by Step</title>
      <dc:creator>zhenye yu</dc:creator>
      <pubDate>Fri, 19 Jun 2026 07:53:45 +0000</pubDate>
      <link>https://dev.to/wanfeng/from-browser-curl-to-clean-python-requests-code-step-by-step-25oh</link>
      <guid>https://dev.to/wanfeng/from-browser-curl-to-clean-python-requests-code-step-by-step-25oh</guid>
      <description>&lt;p&gt;If you've ever opened your browser's DevTools, right-clicked a request in the Network tab, and hit &lt;strong&gt;Copy as cURL&lt;/strong&gt;, you already know the good part: you've captured &lt;em&gt;exactly&lt;/em&gt; what the browser sent — headers, cookies, auth, body and all.&lt;/p&gt;

&lt;p&gt;The annoying part is what comes next. You wanted Python, and instead you're holding a 30-line shell command full of &lt;code&gt;-H&lt;/code&gt; flags and escaped quotes.&lt;/p&gt;

&lt;p&gt;This post walks through porting a real &lt;code&gt;curl&lt;/code&gt; command to &lt;code&gt;requests&lt;/code&gt; by hand, one flag at a time, so the mapping is clear. Do it once like this and you'll recognize every piece next time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The raw cURL
&lt;/h2&gt;

&lt;p&gt;Here's a representative command copied straight out of Chrome:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s1"&gt;'https://api.example.com/v2/search?lang=en&amp;amp;page=2'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'accept: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjQyfQ.sig'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'content-type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'referer: https://app.example.com/dashboard'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'user-agent: Mozilla/5.0'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-b&lt;/span&gt; &lt;span class="s1"&gt;'session=abc123; theme=dark'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-raw&lt;/span&gt; &lt;span class="s1"&gt;'{"q":"nginx","filters":["open"]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's take it apart.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Method and URL
&lt;/h2&gt;

&lt;p&gt;There is no &lt;code&gt;-X&lt;/code&gt; flag here, but there &lt;em&gt;is&lt;/em&gt; a body (&lt;code&gt;--data-raw&lt;/code&gt;). That detail decides the method: &lt;code&gt;--data&lt;/code&gt; / &lt;code&gt;--data-raw&lt;/code&gt; makes curl use &lt;strong&gt;POST&lt;/strong&gt; by default, unless you explicitly override the method with flags like &lt;code&gt;-X GET&lt;/code&gt;. With &lt;code&gt;-G&lt;/code&gt;, curl moves the data into the query string instead.&lt;/p&gt;

&lt;p&gt;So don't read the missing &lt;code&gt;-X&lt;/code&gt; as "GET" — the presence of a body is what matters.&lt;/p&gt;

&lt;p&gt;The URL also carries a query string, &lt;code&gt;?lang=en&amp;amp;page=2&lt;/code&gt;. We'll handle that as real parameters in a moment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.example.com/v2/search&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Headers → a dict
&lt;/h2&gt;

&lt;p&gt;Every &lt;code&gt;-H 'key: value'&lt;/code&gt; becomes one entry in a &lt;code&gt;headers&lt;/code&gt; dict. The one rule that bites people: split each header on the &lt;strong&gt;first&lt;/strong&gt; colon, not every colon.&lt;/p&gt;

&lt;p&gt;Header values can legitimately contain colons. Look at the &lt;code&gt;referer&lt;/code&gt; here, whose value is a full URL: &lt;code&gt;https://app.example.com/dashboard&lt;/code&gt;. A blanket split on &lt;code&gt;:&lt;/code&gt; would mangle it into pieces; &lt;code&gt;split(":", 1)&lt;/code&gt; keeps the value intact.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;accept&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjQyfQ.sig&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content-type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;referer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://app.example.com/dashboard&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user-agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mozilla/5.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Cookies (&lt;code&gt;-b&lt;/code&gt;) → their own dict
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;-b&lt;/code&gt; flag is a semicolon-separated cookie string. Don't jam it into the headers dict as a raw &lt;code&gt;Cookie&lt;/code&gt; line — hand it to &lt;code&gt;requests&lt;/code&gt; as a proper &lt;code&gt;cookies&lt;/code&gt; argument and let the library do the encoding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cookies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;abc123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;theme&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dark&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. The JSON body: &lt;code&gt;json=&lt;/code&gt; vs &lt;code&gt;data=&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;content-type&lt;/code&gt; is &lt;code&gt;application/json&lt;/code&gt;, so the body is JSON. In &lt;code&gt;requests&lt;/code&gt;, that means you pass it as &lt;code&gt;json=&lt;/code&gt; — &lt;strong&gt;not&lt;/strong&gt; &lt;code&gt;data=&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nginx&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;filters&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why the distinction matters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;json=payload&lt;/code&gt; serializes the dict to a JSON string and sets &lt;code&gt;Content-Type: application/json&lt;/code&gt; for you when you do not override it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;data=payload&lt;/code&gt; form-encodes it as &lt;code&gt;q=nginx&amp;amp;filters=open&lt;/code&gt;, which a JSON endpoint will reject or misread.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the original request had been &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt;, you'd flip to &lt;code&gt;data=&lt;/code&gt;. Reading the content type tells you which one to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Query params
&lt;/h2&gt;

&lt;p&gt;You &lt;em&gt;could&lt;/em&gt; leave &lt;code&gt;?lang=en&amp;amp;page=2&lt;/code&gt; glued onto the URL string, but pulling it into a &lt;code&gt;params&lt;/code&gt; dict is cleaner and lets &lt;code&gt;requests&lt;/code&gt; handle the encoding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lang&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;page&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One caution: if the params in the original URL are already percent-encoded — you see &lt;code&gt;%20&lt;/code&gt;, &lt;code&gt;%3D&lt;/code&gt;, etc. — don't decode them and pass them through &lt;code&gt;params&lt;/code&gt; as well, or you'll double-encode. Either keep the encoded values in the URL string, or pass the decoded values through &lt;code&gt;params&lt;/code&gt;, but not both.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Putting it together
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.example.com/v2/search&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lang&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;page&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;accept&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjQyfQ.sig&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content-type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;referer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://app.example.com/dashboard&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user-agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mozilla/5.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;cookies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;abc123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;theme&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dark&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nginx&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;filters&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;

&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;cookies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because &lt;code&gt;json=&lt;/code&gt; sets the content type for you when you do not override it, you could often drop the explicit &lt;code&gt;content-type&lt;/code&gt; header and get the same request on the wire.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gotchas that eat an afternoon
&lt;/h2&gt;

&lt;p&gt;A few things that don't show up until they break:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Assuming GET because there's no &lt;code&gt;-X&lt;/code&gt;.&lt;/strong&gt; If &lt;code&gt;--data&lt;/code&gt; / &lt;code&gt;--data-raw&lt;/code&gt; is present, curl uses POST by default unless you override it with &lt;code&gt;-X GET&lt;/code&gt; or use &lt;code&gt;-G&lt;/code&gt; to move data into the query string.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;json=&lt;/code&gt; vs &lt;code&gt;data=&lt;/code&gt;.&lt;/strong&gt; The single most common mistake. Match it to the content type.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multipart uploads (&lt;code&gt;-F&lt;/code&gt;).&lt;/strong&gt; These map to &lt;code&gt;files=&lt;/code&gt;, not &lt;code&gt;data=&lt;/code&gt;. Mixing them up gives you a malformed boundary and a baffling 400.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Splitting headers on every colon.&lt;/strong&gt; Split once; values like URLs and timestamps contain colons of their own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Double-encoding query params.&lt;/strong&gt; Don't both decode the URL and pass the same values through &lt;code&gt;params&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compression headers.&lt;/strong&gt; &lt;code&gt;requests&lt;/code&gt; handles common decompression for you, and brotli support depends on your installed stack, so you can usually drop &lt;code&gt;accept-encoding&lt;/code&gt; unless you are testing compression behavior specifically.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When you just want it done
&lt;/h2&gt;

&lt;p&gt;Doing this by hand once is worth it — now you know what each flag maps to. After that, it's mostly tax, especially for requests with twenty-plus headers.&lt;/p&gt;

&lt;p&gt;For the repetitive cases I keep a small browser-based converter open: paste the cURL, get &lt;code&gt;requests&lt;/code&gt; code back. It runs entirely client-side, so the command — and any live auth tokens in it — never leaves the browser:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://opskit.cc/tools/curl-to-requests?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=curl_to_requests_guide" rel="noopener noreferrer"&gt;curl to Python requests converter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're dropping the request into a crawler rather than a one-off script, there's also a low-key &lt;a href="https://opskit.cc/tools/curl-to-feapder?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=curl_to_requests_guide" rel="noopener noreferrer"&gt;Feapder spider mode&lt;/a&gt; that emits a ready-to-run spider class.&lt;/p&gt;




&lt;p&gt;What's the gnarliest cURL you've had to port to Python by hand? Mine had a multipart body and eleven cookies. Curious what trips other people up most — &lt;code&gt;json=&lt;/code&gt; and &lt;code&gt;data=&lt;/code&gt;, or something else?&lt;/p&gt;

</description>
      <category>python</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>api</category>
    </item>
    <item>
      <title>cron</title>
      <dc:creator>zhenye yu</dc:creator>
      <pubDate>Fri, 19 Jun 2026 06:10:02 +0000</pubDate>
      <link>https://dev.to/wanfeng/cron-5de5</link>
      <guid>https://dev.to/wanfeng/cron-5de5</guid>
      <description></description>
    </item>
  </channel>
</rss>
