Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
requests.exceptions.MissingSchema means Requests could not find a URL scheme—usually https:// or http://—in the URL it was preparing. Pass a complete absolute URL, inspect the exact value immediately before the request, and check proxy configuration separately if you use one. In Requests 2.x, a proxy URL also needs a scheme.
What MissingSchema means
Requests prepares and parses a URL before it can send an HTTP request. If the parsed URL has no scheme, Requests raises MissingSchema. A typical message is Invalid URL 'example.com': No scheme supplied. Perhaps you meant https://example.com? The important clue is No scheme supplied: Requests has not been given a complete web address.
A scheme is the part before the host that tells a client how to address the resource, commonly https:// or http://. For an ordinary web request, use the scheme that is correct for the site. Do not assume that every site supports plain HTTP just because adding http:// makes the string look complete.
This is distinct from an address that has a scheme but no host. In Requests 2.34.2, that malformed-URL path raises InvalidURL with No host supplied, rather than MissingSchema. The exception name and message help decide whether to add a scheme or inspect a different part of the URL.
#1 Best Overall
Fix the destination URL
1. Inspect the value that actually reaches Requests
The value in a source file, debugger watch, or earlier log is not necessarily the value passed to requests.get(). Log the runtime value and its type immediately before the call:
import requests
url = "https://example.com"
print("url:", repr(url), "type:", type(url).__name__)
response = requests.get(url, timeout=15)
print(response.status_code)
repr(url) makes invisible whitespace and some unexpected values easier to notice; the type check helps catch a value that is not a string. Confirm that it is the expected non-empty absolute URL, rather than None, an empty string, a relative path, or a different variable than the one you meant to use. Once diagnosed, remove or appropriately protect debugging output; URLs can contain sensitive query values.
2. Add the correct scheme to a hostname
A hostname and path such as example.com/products are not a complete absolute web URL. Use the full address:
Rank #2
import requests
url = "https://example.com/products"
response = requests.get(url, timeout=15)
response.raise_for_status()
print(response.url)
response.url shows the URL associated with the resulting response, which can help confirm what Requests used. raise_for_status() makes an HTTP error status visible as an exception; it does not correct a malformed URL. Likewise, a timeout controls how long the request waits, not whether the URL has a scheme.
3. Resolve relative links before requesting them
A scraped link such as /products/1 is relative to a page, not independently requestable as an absolute web address. Combine it with the page URL that supplied the link, then inspect the result:
from urllib.parse import urljoin
import requests
page_url = "https://example.com/catalog/"
href = "/products/1"
target_url = urljoin(page_url, href)
print(repr(target_url))
response = requests.get(target_url, timeout=15)
response.raise_for_status()
For this example, the resolved target is https://example.com/products/1. Use the actual page URL as the base: a relative link’s destination depends on its base, and blindly prefixing a scheme to a path does not resolve it correctly. If links come from markup, also check that the extracted value is the expected href and not a missing attribute or a different field.
Check proxy configuration separately
The destination URL is not the only URL-like value Requests may parse. If you configure a proxy, inspect its value independently. Requests 2.x requires a scheme in the proxy address; a bare value such as 10.10.1.10:3128 can result in MissingSchema. Configure it with the appropriate proxy protocol, for example:
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →proxies = {
"http": "http://10.10.1.10:3128",
"https": "http://10.10.1.10:3128",
}
response = requests.get(
"https://example.com",
proxies=proxies,
timeout=15,
)
The example uses an HTTP proxy endpoint for both destination schemes; the destination still uses HTTPS for its own connection. Use the protocol and address supplied for your actual proxy rather than copying the example values. If the exception persists after correcting the destination, temporarily inspect or disable the proxy configuration in a controlled environment to determine which value is malformed.
Keep query parameters separate from the base URL
Requests can encode query values supplied through params, but query parameters do not supply a missing scheme. The base URL must already be absolute:
import requests
url = "https://example.com/search"
params = {"q": "python requests", "page": 2}
response = requests.get(url, params=params, timeout=15)
print(response.url)
This separation is useful when values may contain spaces or other characters that need encoding. It does not make example.com/search valid as the base URL; use https://example.com/search first. If you construct the URL yourself, inspect the finished value before making the request.
Trace the value back to its source
If inspection reveals None, an empty string, or an unexpected relative route, fix the point where that value is produced rather than patching it blindly at the request call. Common places to check include configuration loading, function arguments, scraped attributes, environment variables, and string transformations. A value that was expected to be present can still be absent at runtime.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
def fetch_page(url):
if not isinstance(url, str) or not url.strip():
raise ValueError(f"Expected a non-empty URL string; got {url!r}")
if not url.startswith(("http://", "https://")):
raise ValueError(f"Expected an absolute HTTP(S) URL; got {url!r}")
return requests.get(url, timeout=15)
response = fetch_page("https://example.com")
This guard is deliberately narrow: it catches common input mistakes before the request. It is not a complete URL validator, and a string passing this check can still have a missing host or another malformed component. If your application accepts other valid schemes or has a different URL policy, adjust the check to match that policy.
Best Value
Diagnose the error by its source
| What you find | Likely issue | Next step |
|---|---|---|
example.com/path |
The destination has no scheme. | Use the correct absolute address, such as https://example.com/path. |
/products/1 |
The value is a relative path. | Resolve it against the page’s absolute URL before requesting it. |
None, '', or an unexpected value |
The runtime input is missing or differs from what was expected. | Trace its assignment or extraction; validate it before the call. |
A proxy value such as 10.10.1.10:3128 |
The proxy address lacks a scheme. | Use the appropriate scheme and verify the proxy’s actual configuration. |
InvalidURL: No host supplied |
The URL may have a scheme but no host. | Inspect the complete URL; this is not the missing-scheme case. |
To narrow down a persistent problem, check in this order: the destination passed to the request, any transformation that creates it, and proxy settings. Test with a known absolute URL only when doing so is safe and appropriate for your environment. If that works but the application value fails, focus on the application’s input path rather than changing unrelated request settings.
Or skip the browser setup
If your actual task is to get a screenshot of a web page rather than build a browser-capture workflow, ScreenshotNeo is a website screenshot API and MCP server. A request still needs an absolute URL; the example below uses the supplied endpoint and target. See the ScreenshotNeo documentation for request options.
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
Here are the equivalent one-request examples in cURL and Node.js:
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallcurl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
- Cookie banners are accepted before capture, and 60+ known consent platforms, newsletter popups, and chat widgets are removed; each step can be turned off.
- Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed. Responses identify the page verdict and billing status in headers.
- An MCP server provides
take_screenshot,get_page_info, andcapture_pdftools for AI agents and MCP clients. - The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up for ScreenshotNeo’s free plan to try 1,000 screenshots a month with no card.
Frequently Asked Questions
Does MissingSchema mean the website returned an error?
No. It is raised while Requests is preparing the URL, before a normal HTTP response from the destination is available.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

