<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Daily Python Projects: Learn Software Development]]></title><description><![CDATA[Learn the concepts beyond coding and syntax - webhooks, security, deployments, databases, and many more.]]></description><link>https://dailypythonprojects.substack.com/s/learn-software-development</link><image><url>https://substackcdn.com/image/fetch/$s_!gfXP!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5e0e0918-4155-47ff-ba5d-c061609cbc83_1000x1000.png</url><title>Daily Python Projects: Learn Software Development</title><link>https://dailypythonprojects.substack.com/s/learn-software-development</link></image><generator>Substack</generator><lastBuildDate>Fri, 24 Jul 2026 20:02:55 GMT</lastBuildDate><atom:link href="https://dailypythonprojects.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Ardit Sulce]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[dailypythonprojects@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[dailypythonprojects@substack.com]]></itunes:email><itunes:name><![CDATA[Ardit Sulce]]></itunes:name></itunes:owner><itunes:author><![CDATA[Ardit Sulce]]></itunes:author><googleplay:owner><![CDATA[dailypythonprojects@substack.com]]></googleplay:owner><googleplay:email><![CDATA[dailypythonprojects@substack.com]]></googleplay:email><googleplay:author><![CDATA[Ardit Sulce]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Webhooks, taught by a script that texts you]]></title><description><![CDATA[Polling is calling the pizza place every two minutes. A webhook is them calling you.]]></description><link>https://dailypythonprojects.substack.com/p/webhooks-taught-by-a-script-that</link><guid isPermaLink="false">https://dailypythonprojects.substack.com/p/webhooks-taught-by-a-script-that</guid><dc:creator><![CDATA[Ardit Sulce]]></dc:creator><pubDate>Thu, 23 Jul 2026 13:49:47 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Ng-_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9becdad5-f680-407f-a7a0-1df2dd95b6f5_2400x960.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>&#128227; Starting today, Thursdays on Daily Python Projects are getting something new. On Tuesdays and Wednesdays we build projects, but I have noticed over the years that most Python learners are missing something else entirely: they can write the code, but nobody ever explained to them how software actually gets built and shipped. What is a server, how do you deploy a script, what is Docker, where does the data live. So every Thursday, we will take one of these topics and demystify it. Today we start with webhooks.&#127881; </em></p><div><hr></div><p>In this post, you will learn what webhooks are and how to use one to send notifications from your scripts straight to your phone. Webhooks are one of the most common building blocks in software development, and once you understand them, a big part of modern automation stops being mysterious.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ng-_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9becdad5-f680-407f-a7a0-1df2dd95b6f5_2400x960.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ng-_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9becdad5-f680-407f-a7a0-1df2dd95b6f5_2400x960.png 424w, https://substackcdn.com/image/fetch/$s_!Ng-_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9becdad5-f680-407f-a7a0-1df2dd95b6f5_2400x960.png 848w, https://substackcdn.com/image/fetch/$s_!Ng-_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9becdad5-f680-407f-a7a0-1df2dd95b6f5_2400x960.png 1272w, https://substackcdn.com/image/fetch/$s_!Ng-_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9becdad5-f680-407f-a7a0-1df2dd95b6f5_2400x960.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ng-_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9becdad5-f680-407f-a7a0-1df2dd95b6f5_2400x960.png" width="1456" height="582" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9becdad5-f680-407f-a7a0-1df2dd95b6f5_2400x960.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:582,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:74371,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://dailypythonprojects.substack.com/i/208186535?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9becdad5-f680-407f-a7a0-1df2dd95b6f5_2400x960.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Ng-_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9becdad5-f680-407f-a7a0-1df2dd95b6f5_2400x960.png 424w, https://substackcdn.com/image/fetch/$s_!Ng-_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9becdad5-f680-407f-a7a0-1df2dd95b6f5_2400x960.png 848w, https://substackcdn.com/image/fetch/$s_!Ng-_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9becdad5-f680-407f-a7a0-1df2dd95b6f5_2400x960.png 1272w, https://substackcdn.com/image/fetch/$s_!Ng-_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9becdad5-f680-407f-a7a0-1df2dd95b6f5_2400x960.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>You have probably created an automation with Python before. Perhaps a script that sends an email, or something that scrapes data from a weather website. Now imagine the script crashed for some reason. You would not know immediately. You might come back days later and see that no data has been scraped for a week.</p><p>So what most people do is add a loop somewhere that keeps checking:</p><pre><code><code>import time

while True:
    if check_if_done():
        break
    time.sleep(10)
</code></code></pre><p>This approach is called polling. Your script keeps asking &#8220;is it done yet?&#8221; over and over. It works, but think about what is actually happening here: your script sends thousands of requests a day just to hear &#8220;no&#8221; almost every time. It is like calling the pizza place every two minutes to ask if your order is ready. They will not enjoy that, and neither will the server you are polling.</p><p>The pizza place solves this differently, of course. You give them your number, and they call you when the order is ready. In software, that pattern is called a webhook.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bfb_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a552541-6a06-4b76-ba76-d19bac154c7e_2400x1080.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bfb_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a552541-6a06-4b76-ba76-d19bac154c7e_2400x1080.png 424w, https://substackcdn.com/image/fetch/$s_!bfb_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a552541-6a06-4b76-ba76-d19bac154c7e_2400x1080.png 848w, https://substackcdn.com/image/fetch/$s_!bfb_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a552541-6a06-4b76-ba76-d19bac154c7e_2400x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!bfb_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a552541-6a06-4b76-ba76-d19bac154c7e_2400x1080.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bfb_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a552541-6a06-4b76-ba76-d19bac154c7e_2400x1080.png" width="1456" height="655" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8a552541-6a06-4b76-ba76-d19bac154c7e_2400x1080.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:655,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!bfb_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a552541-6a06-4b76-ba76-d19bac154c7e_2400x1080.png 424w, https://substackcdn.com/image/fetch/$s_!bfb_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a552541-6a06-4b76-ba76-d19bac154c7e_2400x1080.png 848w, https://substackcdn.com/image/fetch/$s_!bfb_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a552541-6a06-4b76-ba76-d19bac154c7e_2400x1080.png 1272w, https://substackcdn.com/image/fetch/$s_!bfb_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a552541-6a06-4b76-ba76-d19bac154c7e_2400x1080.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>A webhook is not a technology</h2><p>So what is a webhook exactly? It is an HTTP request that someone else&#8217;s computer sends to a URL of your choice when an event happens. That is really the whole definition. It is not a library you install or a protocol you have to study. It is just an agreement between two computers: when X happens, send a POST request to this address.</p><p>Until now, you have always been the caller. Every <code>requests.get()</code> you have written is your script phoning a server. A webhook is the same thing in the opposite direction: the server phones you.</p><h2>The script that texts you</h2><p>Let me show you the fastest way to experience this. We will make a script send a notification to your phone.</p><p>The tool we will use is ntfy.sh. There is no signup, no API key, and no credit card involved. I have seen enough free services die under my projects to be careful with recommendations, but ntfy has been reliable for me.</p><p>First, install the ntfy app on your phone (free on iOS and Android) and subscribe to a topic. A topic is just a name you invent. Keep in mind that anyone who knows the name can send messages to it, so pick something hard to guess, like <code>da-topic-4821</code>, not <code>test</code>.</p><p>Then run this script:</p><pre><code><code>import requests

requests.post(
    "https://ntfy.sh/da-topic-4821",
    data="Scraper finished. 340 rows."
)
</code></code></pre><p>About one second later, your phone buzzes.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8c4Y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32fb53c5-e808-4cba-aec0-a1c161abc65e_2400x1120.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8c4Y!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32fb53c5-e808-4cba-aec0-a1c161abc65e_2400x1120.png 424w, https://substackcdn.com/image/fetch/$s_!8c4Y!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32fb53c5-e808-4cba-aec0-a1c161abc65e_2400x1120.png 848w, https://substackcdn.com/image/fetch/$s_!8c4Y!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32fb53c5-e808-4cba-aec0-a1c161abc65e_2400x1120.png 1272w, https://substackcdn.com/image/fetch/$s_!8c4Y!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32fb53c5-e808-4cba-aec0-a1c161abc65e_2400x1120.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8c4Y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32fb53c5-e808-4cba-aec0-a1c161abc65e_2400x1120.png" width="1456" height="679" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/32fb53c5-e808-4cba-aec0-a1c161abc65e_2400x1120.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:679,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!8c4Y!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32fb53c5-e808-4cba-aec0-a1c161abc65e_2400x1120.png 424w, https://substackcdn.com/image/fetch/$s_!8c4Y!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32fb53c5-e808-4cba-aec0-a1c161abc65e_2400x1120.png 848w, https://substackcdn.com/image/fetch/$s_!8c4Y!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32fb53c5-e808-4cba-aec0-a1c161abc65e_2400x1120.png 1272w, https://substackcdn.com/image/fetch/$s_!8c4Y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F32fb53c5-e808-4cba-aec0-a1c161abc65e_2400x1120.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>That is four lines of Python, and your script can now reach you anywhere.</p><h2>What actually traveled over the wire</h2><p>Let&#8217;s look at what your script actually sent to ntfy&#8217;s server.</p><p></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!oXeP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e603ef-b3ef-4753-9b03-bc555b6ae202_2400x1000.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!oXeP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e603ef-b3ef-4753-9b03-bc555b6ae202_2400x1000.png 424w, https://substackcdn.com/image/fetch/$s_!oXeP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e603ef-b3ef-4753-9b03-bc555b6ae202_2400x1000.png 848w, https://substackcdn.com/image/fetch/$s_!oXeP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e603ef-b3ef-4753-9b03-bc555b6ae202_2400x1000.png 1272w, https://substackcdn.com/image/fetch/$s_!oXeP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e603ef-b3ef-4753-9b03-bc555b6ae202_2400x1000.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!oXeP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e603ef-b3ef-4753-9b03-bc555b6ae202_2400x1000.png" width="1456" height="607" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/79e603ef-b3ef-4753-9b03-bc555b6ae202_2400x1000.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:607,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:125690,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://dailypythonprojects.substack.com/i/207925167?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e603ef-b3ef-4753-9b03-bc555b6ae202_2400x1000.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!oXeP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e603ef-b3ef-4753-9b03-bc555b6ae202_2400x1000.png 424w, https://substackcdn.com/image/fetch/$s_!oXeP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e603ef-b3ef-4753-9b03-bc555b6ae202_2400x1000.png 848w, https://substackcdn.com/image/fetch/$s_!oXeP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e603ef-b3ef-4753-9b03-bc555b6ae202_2400x1000.png 1272w, https://substackcdn.com/image/fetch/$s_!oXeP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79e603ef-b3ef-4753-9b03-bc555b6ae202_2400x1000.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>It is a verb (POST), an address, a couple of headers, and a body with the message. Every webhook in the world has this same shape. When Stripe informs an online shop that a payment succeeded, it sends this exact structure, just with a bigger JSON body. When GitHub tells a build server that someone pushed code, same thing.</p><h2>The other side of the phone call</h2><p>So far you have been the sender. Receiving a webhook is the reverse: you have a URL, and other computers send POST requests to it when something happens.</p><p>You might think you need Flask or FastAPI for this, but you don&#8217;t. Python comes with everything built in:</p><pre><code><code>from http.server import HTTPServer, BaseHTTPRequestHandler

class Hook(BaseHTTPRequestHandler):
    def do_POST(self):
        length = int(self.headers["Content-Length"])
        body = self.rfile.read(length).decode()
        print("Webhook received:", body)
        self.send_response(200)
        self.end_headers()

HTTPServer(("", 8000), Hook).serve_forever()
</code></code></pre><p>Run this and you have a webhook receiver at http://localhost:8000. If you send it a POST request from a second terminal (reuse the four-line script from above and just change the URL), you will see the message printed. You just ran both sides of a webhook on one machine, with the standard library only.</p><p>By the way, if you want to watch a real webhook arrive before writing any code, open webhook.site in your browser. It gives you a temporary URL and shows every request that hits it, live. Five minutes there and webhooks will never feel mysterious again.</p><h2>The catch: localhost is a private address</h2><p>There is one thing that catches almost everyone at this point. Your receiver works when you send to it. However, if you paste http://localhost:8000 into Stripe&#8217;s webhook settings, nothing will ever arrive.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IOQf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03d21dc2-d454-4068-961f-34cb39dd2d77_2400x920.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IOQf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03d21dc2-d454-4068-961f-34cb39dd2d77_2400x920.png 424w, https://substackcdn.com/image/fetch/$s_!IOQf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03d21dc2-d454-4068-961f-34cb39dd2d77_2400x920.png 848w, https://substackcdn.com/image/fetch/$s_!IOQf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03d21dc2-d454-4068-961f-34cb39dd2d77_2400x920.png 1272w, https://substackcdn.com/image/fetch/$s_!IOQf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03d21dc2-d454-4068-961f-34cb39dd2d77_2400x920.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IOQf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03d21dc2-d454-4068-961f-34cb39dd2d77_2400x920.png" width="1456" height="558" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/03d21dc2-d454-4068-961f-34cb39dd2d77_2400x920.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:558,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:104411,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://dailypythonprojects.substack.com/i/207925167?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03d21dc2-d454-4068-961f-34cb39dd2d77_2400x920.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!IOQf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03d21dc2-d454-4068-961f-34cb39dd2d77_2400x920.png 424w, https://substackcdn.com/image/fetch/$s_!IOQf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03d21dc2-d454-4068-961f-34cb39dd2d77_2400x920.png 848w, https://substackcdn.com/image/fetch/$s_!IOQf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03d21dc2-d454-4068-961f-34cb39dd2d77_2400x920.png 1272w, https://substackcdn.com/image/fetch/$s_!IOQf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03d21dc2-d454-4068-961f-34cb39dd2d77_2400x920.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>That is because localhost means &#8220;this machine&#8221;. It is a name only your own computer can hear. Your laptop has no public address on the internet. It sits behind your router, and the router has no idea it should forward anything to you.</p><p>There are two solutions. For testing, you can use a tunnel like ngrok, which gives you a temporary public URL that forwards to your laptop. For real applications, you deploy the receiver to a server, which is simply a computer with a public address that stays on. That trip from laptop to server is a topic we will keep coming back to in this publication.</p><h2>The pattern to steal today</h2><p>Here is what I recommend you do with all this. Wrap the sender in a small function and keep it in every project:</p><pre><code><code>import requests

def notify(message):
    requests.post("https://ntfy.sh/da-topic-4821", data=message)
</code></code></pre><pre><code><code>notify("Backup complete: 2.3 GB")
notify(f"Script crashed: {error}")
notify("Price dropped below 40 EUR")
</code></code></pre><p>My personal rule is that any script running longer than a coffee gets a <code>notify()</code> call at the end and one in the error handler. It changes how you work, because your scripts stop being things you watch and start being things that report to you.</p><p>And once you know the shape, you will notice webhooks everywhere: GitHub triggering tests, Stripe unlocking accounts, Telegram bots replying to messages.</p><p>In a future post, we will take the next step: giving your own function a public URL, so that anything on the internet can call your code.</p><p>-Ardit</p>]]></content:encoded></item></channel></rss>