π Async API Requests
Async API requests allow Python to send multiple API requests concurrently, so the program doesn't have to wait for one request to finish before starting another.
A common library for this is aiohttp.
π¦ Install
pip install aiohttpπ₯ Basic Async GET Request
import asyncio
import aiohttp
async def get_data():
async with aiohttp.ClientSession() as session:
async with session.get("https://api.example.com/data") as response:
data = await response.json()
print(data)
asyncio.run(get_data())Here:
async def β Async function
session.get() β Send API request
await β Wait for response without blocking
response.json() β Read JSON responseπ Multiple API Requests
import asyncio
import aiohttp
async def get_data(session, url):
async with session.get(url) as response:
return await response.json()
async def main():
urls = [
"https://api.example.com/1",
"https://api.example.com/2",
"https://api.example.com/3"
]
async with aiohttp.ClientSession() as session:
results = await asyncio.gather(
*(get_data(session, url) for url in urls)
)
print(results)
asyncio.run(main())
β Remember
Async API requests = Send multiple network requests efficiently without blocking while waiting for responses.
<div class="infographic-pyaiohttp">
<style>
.infographic-pyaiohttp {
--ink:#1a1a1a; --ink-soft:#5c5c5c; --paper:#fff; --paper-alt:#f0ece4;
--accent:#2563eb; --accent-soft:#e3ebfd; --line:#ddd6c9;
--ok:#1f9d55; --ok-soft:#e3f5ec;
--mono-font:'IBM Plex Mono','SFMono-Regular',Menlo,monospace;
--display-font:'Fraunces',Georgia,serif; --body-font:'Inter',system-ui,sans-serif;
box-sizing:border-box; max-width:680px; margin:0 auto; font-family:var(--body-font); color:var(--ink); background:var(--paper);
}
.infographic-pyaiohttp[data-theme="dark"] {
--ink:#e8e6e1; --ink-soft:#9a968c; --paper:#121212; --paper-alt:#1e1e26;
--accent:#5b8def; --accent-soft:#202942; --line:#2c313d;
--ok:#6fdc8c; --ok-soft:#16342a;
}
.infographic-pyaiohttp *,.infographic-pyaiohttp *::before,.infographic-pyaiohttp *::after{box-sizing:border-box;}
.infographic-pyaiohttp .ah-wrap{padding:0 clamp(1rem,4vw,2rem) 1.5rem;}
.infographic-pyaiohttp h1{font-family:var(--display-font);font-weight:600;font-size:clamp(1.35rem,5vw,1.8rem);text-align:center;margin:1.5rem 0 0.25rem;letter-spacing:-0.01em;}
.infographic-pyaiohttp .ah-sub{text-align:center;color:var(--ink-soft);font-size:0.85rem;margin:0 0 1.3rem;}
.infographic-pyaiohttp .code-block{font-family:var(--mono-font);font-size:0.72rem;line-height:1.65;white-space:pre;background:var(--paper-alt);border:1px solid var(--line);border-radius:8px;padding:0.8rem 0.9rem;margin-bottom:1.1rem;overflow-x:auto;}
.infographic-pyaiohttp .code-block .kw{color:var(--accent);font-weight:700;}
.infographic-pyaiohttp .code-block .aio{font-weight:700;color:#9333ea;}
.infographic-pyaiohttp[data-theme="dark"] .code-block .aio{color:#c58af2;}
.infographic-pyaiohttp .ln{border-radius:4px;padding:0.02rem 0.12rem;transition:background .3s,box-shadow .3s;}
.infographic-pyaiohttp .ln.is-on{background:var(--accent-soft);box-shadow:0 0 0 1.5px var(--accent);}
.infographic-pyaiohttp .task-row{display:flex;align-items:center;gap:0.6rem;margin-bottom:0.6rem;}
.infographic-pyaiohttp .task-label{font-family:var(--mono-font);font-size:0.76rem;width:190px;flex:none;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.infographic-pyaiohttp .task-track{flex:1;height:18px;background:var(--paper-alt);border:1px solid var(--line);border-radius:999px;overflow:hidden;}
.infographic-pyaiohttp .task-fill{height:100%;width:0%;background:var(--accent);border-radius:999px;transition:width linear;}
.infographic-pyaiohttp .task-fill.is-done{background:var(--ok);}
.infographic-pyaiohttp .task-time{font-family:var(--mono-font);font-size:0.68rem;color:var(--ink-soft);width:34px;text-align:right;flex:none;}
.infographic-pyaiohttp .run-row{display:flex;justify-content:center;align-items:center;gap:1rem;margin:1rem 0 0.7rem;}
.infographic-pyaiohttp .run-btn{font:inherit;font-family:var(--mono-font);font-size:0.85rem;font-weight:700;background:var(--accent);color:#fff;border:none;border-radius:999px;padding:0.55rem 1.2rem;cursor:pointer;}
.infographic-pyaiohttp .run-btn:disabled{opacity:0.5;cursor:default;}
.infographic-pyaiohttp .stopwatch{font-family:var(--display-font);font-size:1.3rem;font-weight:600;color:var(--accent);}
.infographic-pyaiohttp .device{--ed-bg:#1e1e1e;--ed-bar:#2d2d2d;--ed-text:#d4d4d4;--ed-soft:#8a8a8a;--ed-ok:#6fdc8c;background:var(--ed-bg);border:1px solid var(--line);border-radius:8px;overflow:hidden;}
.infographic-pyaiohttp .device-bar{display:flex;align-items:center;gap:0.4rem;background:var(--ed-bar);padding:0.5rem 0.7rem;border-bottom:1px solid #3a3a3a;font-size:0.72rem;color:var(--ed-soft);font-weight:600;}
.infographic-pyaiohttp .device-bar .dot{width:9px;height:9px;border-radius:50%;background:#5a5a5a;}
.infographic-pyaiohttp .device-bar .dot:nth-child(1){background:#ff5f56;}
.infographic-pyaiohttp .device-body{padding:0.75rem 0.9rem;font-family:var(--mono-font);font-size:0.76rem;color:var(--ed-text);min-height:90px;max-height:210px;overflow-y:auto;white-space:pre-wrap;}
.infographic-pyaiohttp .log-line{line-height:1.65;opacity:0;animation:ah-fadein .2s ease forwards;}
@keyframes ah-fadein{from{opacity:0;}to{opacity:1;}}
.infographic-pyaiohttp .log-empty{color:var(--ed-soft);}
.infographic-pyaiohttp .ah-caption{text-align:center;font-size:0.85rem;color:var(--ink-soft);margin-top:0.9rem;}
@media (prefers-reduced-motion:reduce){.infographic-pyaiohttp *,.infographic-pyaiohttp *::before,.infographic-pyaiohttp *::after{animation-duration:.001ms!important;transition-duration:.001ms!important;}}
</style>
<div class="ah-wrap">
<h1>Concurrent Requests with aiohttp</h1>
<p class="ah-sub">Three network calls, one session, fired together instead of one at a time.</p>
<div class="code-block"><span class="kw">async def</span> get_data(session, url):
<span class="kw">async with</span> <span class="ln" id="lnGet">session.get(url)</span> <span class="kw">as</span> response:
<span class="kw">return</span> <span class="ln" id="lnJson">await response.json()</span>
<span class="kw">async def</span> main():
urls = [url1, url2, url3]
<span class="kw">async with</span> <span class="ln" id="lnSession">aiohttp.ClientSession()</span> <span class="kw">as</span> session:
results = <span class="kw">await</span> <span class="ln aio" id="lnGather">asyncio.gather(</span>
*(get_data(session, url) <span class="kw">for</span> url <span class="kw">in</span> urls)
<span class="ln aio">)</span>
<span class="ln" id="lnPrint">print(results)</span>
asyncio.run(main())</div>
<div class="task-row">
<span class="task-label">/1 β {"id": 1, ...}</span>
<div class="task-track"><div class="task-fill" id="fill1"></div></div>
<span class="task-time">1.2s</span>
</div>
<div class="task-row">
<span class="task-label">/2 β {"id": 2, ...}</span>
<div class="task-track"><div class="task-fill" id="fill2"></div></div>
<span class="task-time">2.0s</span>
</div>
<div class="task-row">
<span class="task-label">/3 β {"id": 3, ...}</span>
<div class="task-track"><div class="task-fill" id="fill3"></div></div>
<span class="task-time">0.8s</span>
</div>
<div class="run-row">
<button class="run-btn" id="runBtn">βΆ Run: asyncio.run(main())</button>
<span class="stopwatch" id="stopwatch">0.0s</span>
</div>
<div class="device">
<div class="device-bar"><span class="dot"></span><span class="dot"></span><span class="dot"></span> Terminal</div>
<div class="device-body" id="ahLog"><div class="log-empty">Hit Run β watch all three requests fire before any response lands.</div></div>
</div>
<p class="ah-caption">If these ran one after another it'd take 1.2 + 2.0 + 0.8 = 4.0s. Together, it takes about as long as the slowest one β 2.0s.</p>
</div>
<script>
(function(){
var root = document.currentScript.closest('.infographic-pyaiohttp') || document.querySelector('.infographic-pyaiohttp');
var bodyAttr = document.body.getAttribute('data-darkmode');
var dark = bodyAttr === 'true' ? true : bodyAttr === 'false' ? false : (document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/) || [])[1] === 'true';
if (dark) root.setAttribute('data-theme', 'dark');
var runBtn = root.querySelector('#runBtn');
var stopwatch = root.querySelector('#stopwatch');
var ahLog = root.querySelector('#ahLog');
var firstLog = true;
var running = false;
var swInterval = null;
var lnSession = root.querySelector('#lnSession');
var lnGather = root.querySelectorAll('#lnGather, .aio');
var lnGet = root.querySelector('#lnGet');
var lnJson = root.querySelector('#lnJson');
var lnPrint = root.querySelector('#lnPrint');
var allLines = [lnSession, lnGet, lnJson, lnPrint].concat(Array.prototype.slice.call(root.querySelectorAll('.aio')));
var reqs = [
{ key: 'fill1', url: '/1', dur: 1200, json: '{"id": 1, "title": "First"}' },
{ key: 'fill2', url: '/2', dur: 2000, json: '{"id": 2, "title": "Second"}' },
{ key: 'fill3', url: '/3', dur: 800, json: '{"id": 3, "title": "Third"}' }
];
var fills = { fill1: root.querySelector('#fill1'), fill2: root.querySelector('#fill2'), fill3: root.querySelector('#fill3') };
function logLine(text) {
if (firstLog) { ahLog.innerHTML = ''; firstLog = false; }
var line = document.createElement('div');
line.className = 'log-line';
line.textContent = text;
ahLog.appendChild(line);
ahLog.scrollTop = ahLog.scrollHeight;
}
function clearHl() { allLines.forEach(function (l) { l.classList.remove('is-on'); }); }
function resetBars() {
Object.keys(fills).forEach(function (k) {
var el = fills[k];
el.style.transition = 'none'; el.style.width = '0%'; el.classList.remove('is-done');
void el.offsetWidth; el.style.transition = '';
});
stopwatch.textContent = '0.0s';
}
runBtn.addEventListener('click', function () {
if (running) return;
running = true;
runBtn.disabled = true;
clearHl();
resetBars();
firstLog = true;
ahLog.innerHTML = '';
var start = performance.now();
swInterval = setInterval(function () {
stopwatch.textContent = ((performance.now() - start) / 1000).toFixed(1) + 's';
}, 100);
lnSession.classList.add('is-on');
logLine('async with aiohttp.ClientSession() as session:');
setTimeout(function () {
lnSession.classList.remove('is-on');
root.querySelectorAll('.aio').forEach(function (l) { l.classList.add('is-on'); });
logLine('asyncio.gather(get_data(url1), get_data(url2), get_data(url3))');
var doneCount = 0;
reqs.forEach(function (r) {
logLine(' session.get("' + r.url + '") sent');
var el = fills[r.key];
el.style.transitionDuration = (r.dur / 1000) + 's';
requestAnimationFrame(function () { el.style.width = '100%'; });
setTimeout(function () {
el.classList.add('is-done');
logLine(' ' + r.url + ' responded β ' + r.json);
doneCount++;
if (doneCount === reqs.length) {
root.querySelectorAll('.aio').forEach(function (l) { l.classList.remove('is-on'); });
lnPrint.classList.add('is-on');
logLine('print(results)');
logLine('[' + reqs.map(function (x) { return x.json; }).join(', ') + ']');
clearInterval(swInterval);
stopwatch.textContent = ((performance.now() - start) / 1000).toFixed(1) + 's';
setTimeout(function () { lnPrint.classList.remove('is-on'); }, 400);
running = false;
runBtn.disabled = false;
}
}, r.dur);
});
}, 300);
});
})();
</script>
</div>