Your acquisition code cannot open the connection to the network camera, the firewall is off, the ports are open, and the same error comes back: unable to open port. Meanwhile the camera's own web page opens in a browser without complaint. That is the clue and the fix. A camera that serves its live view over HTTP already serves images over HTTP, so the fastest path is to read the snapshot URL with an ordinary HTTP GET instead of opening a raw TCP socket. This guide gives you the two-minute test that proves the service, the way to find the exact URL, working code for single frames and MJPEG streams, a polling budget, and a diagnosis table for the stubborn cases.
Prove the HTTP service before you write code
Work through these five checks on the PC that runs the application. They take two minutes and split the fault in half: camera side or code side.
- Open the camera's address, for example http://192.168.1.100/, in a browser on that PC. If the live view loads, the camera is up, port 80 is open and the route is good. Note the address exactly as the browser shows it; if it carries a port other than 80, the HTTP service was moved at set-up.
- Find the URL the live view page requests. View the page source and look for a path that ends in .jpg, or read the Network tab of the browser's developer tools while the view refreshes. Copy that path character for character.
- Paste the snapshot URL into a fresh browser tab. One still frame should appear; refresh, and the frame should update.
- From a command line on the same PC, run telnet 192.168.1.100 80. A blank screen with a cursor means the TCP connection is accepted. Connection refused means nothing listens on that port; a timeout means a firewall or a wrong address.
- Write the results down. They drop straight into the diagnosis table below.
If the browser shows the frame and telnet connects, the camera and the network are proven. Anything that fails after that point is your code.
Find the snapshot URL when the manual is gone
Two URL types exist, and both arrive over the same HTTP port. A single-frame URL returns one JPEG per GET; an MJPEG URL returns a continuous multipart stream from one GET. You need the first for a poll loop and the second for continuous viewing.
To find either one, open the live view page and then the browser's developer tools on the Network tab, and refresh the view. The row with status 200 and an image content type is your snapshot request; its URL, its GET method and its response headers are everything your code needs. The page source works too: search it for .jpg or for the stream path the embedded player uses, and check the player's properties, which often name the stream path directly.
Keep both URLs if the camera has both. The single-frame address feeds the polling loop in the next section, and the stream address feeds the MJPEG reader after that.
Pull one frame with an HTTP GET
A snapshot URL is a plain HTTP resource: send GET, receive status 200 with a Content-Type of image/jpeg and the encoded bytes. Every HTTP client library does this; the checks around the call are what make it reliable.
import requests
url: str = 'http://192.168.1.100/image.jpg' # the path your browser test returned
timeout_s: float = 1.0 # sized from the polling budget below
r = requests.get(url, timeout=timeout_s) # add auth=('user', 'pass') if the web UI asks for a login
assert r.status_code == 200, f'HTTP {r.status_code}'
assert r.content[:2] == b'\xff\xd8', 'not a JPEG' # JPEG start-of-image marker
print(len(r.content), 'bytes') # your planning size for the budget below
The marker check is the test that proves the result. ff d8 is the first byte pair of every JPEG; if you receive HTML instead, the first bytes differ and the assert fires, which is exactly what a login page or an error page produces. Run the script once next to the browser download and compare the printed byte count with the file size: equal counts mean your pipeline returns the same frame the page shows.
Two details keep the loop honest. Send a no-cache header or append a changing query parameter, or the camera or a proxy may hand you the previous frame. And when the frame also belongs on an operator screen, an HMI client can fetch the same URL on a timer; the HMI recipe for downloading a camera snapshot over HTTP shows that side.
Read the MJPEG stream frame by frame
An MJPEG URL answers one GET with Content-Type multipart/x-mixed-replace and a boundary string, then keeps the connection open and pushes frames separated by that boundary. Two parsing strategies work. If each part carries a Content-Length header, split on the boundary and read the declared length. If it does not, scan the bytes: every JPEG starts with ff d8 and ends with ff d9, so the markers alone delimit frames.
soi: bytes = b'\xff\xd8' # JPEG start-of-image marker
eoi: bytes = b'\xff\xd9' # JPEG end-of-image marker
buf: bytes = stream.read(65536) # one network read from the open response
start: int = buf.find(soi)
end: int = buf.find(eoi, start) + len(eoi)
frame: bytes = buf[start:end] # one complete JPEG, ready to decode
One connection carries every frame, so the wire cost is frame rate times frame size: 15 snapshots/s of 45 kB frames is 15 × 45 kB = 675 kB/s, or 5.4 Mbit/s, about 5% of a switched 100 Mbit/s line. Guard the read loop with a read timeout, so a camera that drops off the network raises an error instead of freezing the thread. What you do with each frame is the same problem as in decoding barcodes from a live RTSP or MJPEG video stream, which picks up where this section ends.
Fix 'unable to open port'
That message means one thing: the TCP connect failed. The camera never accepted the socket, so no HTTP request ever happened. Diagnose at the socket level first, which is what telnet does, then work through the table.
The most expensive wrong turn is the router. Port-forwarding rules only matter when the client sits on a different network from the camera. On one plant LAN, with PC and camera on the same switch or the same subnet, traffic never crosses the router, so forwarding ports 80 and 3015 there changes nothing. Fix addressing or the host firewall instead.
The second classic is protocol. A bare TCP socket that connects to port 80 and then waits sends no request line, so nothing comes back and some toolkits report it as an open failure. Speak HTTP to port 80. The same protocol-match rule applies to a network barcode reader, as the TCP client or listener guide explains.
| What you see | What it means | What to do |
|---|---|---|
| Browser shows the frame, your code fails | Camera and network are fine; the fault is in your code | Send a real HTTP GET with a timeout; check whether your environment routes through a proxy and bypass it for local addresses |
| Browser fails on the same PC | HTTP service down or moved to another port | Read the port from the web UI address bar and test that port with telnet |
| telnet connects, browser fails | Port open, HTTP path wrong | Copy the exact path from the page source or the Network tab |
| telnet refused on one PC, works on another | Host firewall or security software on that PC | Add an outbound allow rule for your application on the camera's HTTP port |
| Works on the office network, not from the line | Different subnet or VLAN between client and camera | Same-LAN traffic needs no router rules; give the client a route or put it on the camera's subnet |
GigE Vision machine-vision cameras fail differently: they announce themselves through discovery, and the matching failure there is ping works, discovery fails, which has its own firewall fix for GigE Vision discovery. For the broader walk-through, the vision camera network connection troubleshooting guide covers the full sequence.
Budget the polling rate before you loop
A poll loop needs three numbers: frame size, cycle time and link load. Measure the first, assume the second conservatively, and the third follows.
Worked example, VGA-class camera on a switched 100 Mbit/s line:
- Frame size: 45 kB = 45,000 B, read from Content-Length after one download.
- Raw link: 100 Mbit/s = 100,000,000 bit/s ÷ 8 bit/B = 12.5 MB/s.
- Planned HTTP goodput at 60% of raw: 12.5 MB/s × 0.6 = 7.5 MB/s.
- Wire time per frame: 45,000 B ÷ 7,500,000 B/s = 0.006 s = 6 ms.
- Round trip and request overhead on a LAN: 1-2 ms; call the transfer phase 10 ms with margin.
- Camera encode and serve time: assume 50 ms until you measure it; time two consecutive browser requests and use the larger value.
Set the client timeout to 1 s, about 16 times the 60 ms cycle, so one slow frame cannot abort the loop. On a Gigabit switch the same frames cost 0.36% of the link, and the ceiling moves to the camera's encode time, not the wire. The decision: 10 snapshots/s of 45 kB frames is comfortable on a quiet 100 Mbit/s line, while 30 snapshots/s starts competing with any MJPEG stream or other traffic on the same switch. When a frame is late or the socket dies, fail fast and log it, following the disconnected-camera detection pattern.
When a snapshot URL is the wrong tool
A snapshot loop suits an IP camera doing monitoring, an occasional still or an operator display. It is not a machine-vision acquisition path: no hardware trigger, no exposure control from your code, and a latency floor set by the camera's encode time rather than by the wire. Inspection stations need the camera's full frame rate with deterministic timing, and that comes from a machine-vision camera through its SDK, on GigE Vision or USB3 Vision, configured through GenICam.
Two cameras and a vision sensor cover that path:
- Sensor format
- 1/1.7"
- Lens mount
- C-mount
- Sensor format
- 1"
- Lens mount
- C-mount
- Interface
- Fast Ethernet
- Resolution
- 5 MP
The MV-CU020-90GC is a 2.5 MP color camera with a global shutter on a C-mount, streaming over GigE. The MV-CS200-10UM moves to 20 MP on a mono 1" sensor over USB3. The MV-SC3050XC-16M-ATN-H is a 5 MP color vision sensor with a global shutter and an M12 mount on Fast Ethernet; inspection runs on the device, no PC in the loop.
For the acquisition code that replaces the poll loop, the hardware trigger grab loop in Python shows the frame-at-a-time pattern with proper triggering. To pick the camera itself, browse the area scan cameras and smart cameras and vision sensors pages, or start the camera configurator from the resolution you need.
Before you deploy: a short checklist
- Camera: a fixed IP address or a DHCP reservation, so the URL cannot break when the lease rotates. Password changed from the default. HTTP port and snapshot path recorded where the team can find them.
- Network: the camera on a switch port negotiated at full speed, on a VLAN separated from office traffic where the line network allows it. No port-forwarding from any router to the internet.
- Application: client timeout at least three times the measured cycle, so 1 s against the 60 ms example. The ff d8 marker checked on every response. Byte counts logged, because a change in frame size is the signature of a cache or a re-encoded stream, and it belongs in the log rather than in a mystery failure.
If the camera in this plan turns out to be the first piece of an inspection station, take the acquisition path from the top: the camera finder filters by interface, resolution and shutter, and the systems topic page carries the rest of the networking and acquisition sequence. For help matching a camera and lens to a real part, reach us through the contact page.
Questions
Which port does an IP camera snapshot URL use?
TCP port 80 is the default for plain HTTP, and the snapshot URL almost always lives on the same port as the web page: if the browser address bar shows 192.168.1.100 with no port, use 80. Port 443 serves HTTPS where the camera offers it. RTSP video is a separate service, conventionally on port 554, and it carries a live stream rather than a single JPEG file.
How many IP cameras can one Fast Ethernet link supply with snapshots?
Budget by bytes. At the worked example's rate, 10 snapshots/s of 45 kB frames costs 450 kB/s per camera, or 3.6 Mbit/s. Twelve cameras therefore load the link with about 43 Mbit/s, under half of a switched 100 Mbit/s line, which is a workable ceiling. Past that, or if the cameras also stream MJPEG continuously, move to a Gigabit switch, where the same frames cost under 1% of the link each.
Why does the snapshot URL keep returning the same frame?
Two usual causes. Your HTTP client or an intermediate proxy cached the response: send a no-cache header or append a changing query parameter to the URL. Or the camera serves its last encoded frame until something asks for a fresh one, and the changing parameter usually forces a new encode. Prove freshness with the lens test: cover the lens, request again, and compare the file size or the image. An identical response means you are reading a cache.
How do I authenticate a snapshot request?
Use the same credentials the web login uses. Most IP cameras accept HTTP basic authentication on the snapshot path, a few use digest authentication, and standard HTTP clients support both: in the Python requests library, the auth=('user', 'pass') argument covers them. The failure signature is distinctive. Without valid credentials the camera returns the login page's HTML, which fails the ff d8 marker check, so a wrong password shows up as not-a-JPEG rather than as a connection error.
Do machine-vision cameras also serve an HTTP snapshot URL?
No. GigE Vision and USB3 Vision cameras deliver frames through their GenICam-based SDK over their own protocol, with hardware trigger and exposure control; their web pages, where present, exist for set-up. If the task is an occasional still or an operator display, an IP camera with a snapshot URL is the simpler tool. If the task is inspection, take frames from the SDK and skip HTTP entirely.


