An easy, memory-safe HTTP client for Free Pascal. Request-FP wraps FPC's HTTP stack with a small API, automatic cleanup, and no application-level dependencies.
Add src to your unit search path and make a request:
uses Request;
var
Response: TResponse;
begin
Response := Http.Get('https://api.example.com/users');
Response.RaiseForStatus;
WriteLn(Response.Text);
end.There is no client to create or free. TResponse cleans itself up.
Use the stateless Http API for one-off requests. Use THttpSession when
requests need to share a base URL, headers, or cookies.
KV is the short syntax for a header, query parameter, form field, or
multipart field:
Response := Http.GetWithParams('https://api.example.com/search', [
KV('q', 'free pascal'),
KV('page', '2')
]);Request-FP percent-encodes names and values as UTF-8.
Response := Http.Get('https://api.example.com/search',
[KV('Authorization', 'Bearer ' + Token)],
[KV('q', 'free pascal')]);The second array is headers and the third is query parameters.
TKeyValue.Create(...) remains available for existing code.
Response := Http.PostForm('https://api.example.com/login', [
KV('email', 'ada@example.com'),
KV('password', Password)
]);PostForm performs the form encoding and sets
application/x-www-form-urlencoded. Use Http.Post when the body is already
encoded or is another content type.
Post a JSON string:
Response := Http.PostJSON(
'https://api.example.com/users',
'{"name":"Ada"}'
);Or pass an existing TJSONData value directly:
uses Request, fpjson;
var
Body: TJSONObject;
begin
Body := TJSONObject.Create;
try
Body.Add('name', 'Ada');
Response := Http.PostJSON('https://api.example.com/users', Body);
finally
Body.Free;
end;
end;Request-FP reads JSON responses lazily:
WriteLn(Response.JSON.FindPath('user.name').AsString);Response owns the parsed value returned by Response.JSON. Do not free that
value yourself. Invalid JSON raises ERequestError with a JSON Parse Error
message.
Response := Http.PostMultipart('https://api.example.com/upload',
[KV('description', 'avatar')],
[KV('file', 'avatar.png')]);The key in the files array is the form field name; the value is the local file path.
Choose the style that fits your program.
Normal methods raise ERequestError for network, TLS, and request failures.
Inspect the response or opt in to raising for a non-2xx status:
try
Response := Http.Get('https://api.example.com/users/42');
Response.RaiseForStatus;
WriteLn(Response.Text);
except
on E: ERequestError do
WriteLn(E.Message);
end;You can also branch without raising:
if Response.OK then
WriteLn(Response.Text)
else
WriteLn('HTTP status: ', Response.StatusCode);Response.OK and Response.IsSuccessStatus are true for status codes 200
through 299.
Every Try* method catches request exceptions:
Result := Http.TryGet('https://api.example.com/users/42');
if Result.OK then
WriteLn(Result.Response.Text)
else if not Result.Success then
WriteLn('Request failed: ', Result.Error)
else
WriteLn('HTTP status: ', Result.Response.StatusCode);Result.Successmeans the HTTP exchange completed, even if the server returned 404 or 500.Result.OKmeans the exchange completed and the status is 2xx.TryGet,TryGetWithParams,TryPost,TryPostForm,TryPostJSON,TryPut,TryDelete, andTryPostMultipartdo not raise request exceptions.
Add Request.Session when calls should share configuration or cookies:
uses Request, Request.Session;
var
Session: THttpSession;
Response: TResponse;
begin
Session.SetBaseURL('https://api.example.com');
Session.SetHeader('Authorization', 'Bearer ' + Token);
Response := Session.Get('/profile');
if Response.OK then
WriteLn(Response.Text);
end.Sessions initialize and clean themselves up automatically. You do not need to
call Session.Init; call it only when you want to reset an existing session to
its defaults.
The base URL works with or without a trailing slash, and paths work with or without a leading slash.
Post JSON with the same convenience as the stateless API:
Response := Session.PostJSON('/profile', '{"displayName":"Ada"}');See the session guide for cookies, timeouts, and the complete API.
- Copy
src/Request.pasand, if needed,src/Request.Session.pasinto your project, or clone this repository. - Add the
srcdirectory to the project's unit search path. - Add
Requestto yourusesclause.
Requirements:
- Free Pascal 3.2.2+ or Lazarus 4.8+
- Windows or Linux
- OpenSSL libraries for HTTPS
On Linux, install the distribution's OpenSSL development package. On Windows,
the OpenSSL DLL architecture must match the executable architecture.
Request-FP v1.3.0 automatically handles FPC 3.2.2's OpenSSL 3 DLL-name issue
and retains its OpenSSL 1.1 fallback; applications do not need to patch FPC.
If HTTPS setup fails, run examples/ssl_debug and follow the
SSL/HTTPS guide.
// Requests
Http.Get(URL)
Http.GetWithParams(URL, Params)
Http.Post(URL, Body)
Http.PostForm(URL, Fields)
Http.PostJSON(URL, JSON)
Http.Put(URL, Body)
Http.Delete(URL)
Http.PostMultipart(URL, Fields, Files)
// Response
Response.StatusCode
Response.Text
Response.JSON
Response.OK
Response.HeaderValue('Content-Type')
Response.RaiseForStatus
Response.SaveToFile('output.dat')Most methods also have header/query-parameter overloads and exception-free
Try* counterparts. See the API reference for exact
signatures.
- Cheat sheet
- Stateless API reference
- Session guide
- SSL/HTTPS guide
- Windows OpenSSL version selection
- Technical details
- Examples
The repository includes scripts that compile every Lazarus example in Release
mode and collect the executables in example-bin/.
Windows PowerShell:
.\build-examples.ps1If script execution is disabled by local policy:
powershell -ExecutionPolicy Bypass -File .\build-examples.ps1Linux or Git Bash:
bash ./build-examples.shBoth scripts require lazbuild on PATH. They discover all .lpi projects
under examples/, skip Lazarus backup directories, and clean example-bin/
before compiling. The generated directory is ignored by Git.
Run an example after building:
.\example-bin\easy_get.exe./example-bin/easy_getBuild the suite with Lazarus or:
lazbuild --build-mode=Release tests/TestRunner.lpi
tests/TestRunner.exe -a --format=plainOn Linux, run tests/TestRunner. CI starts tests/http_fixture.py and points
the suite at that local service, so CI does not depend on a public HTTP test
service. A direct local run without REQUEST_FP_TEST_BASE_URL falls back to
https://httpbin.org and therefore requires network access.
See CONTRIBUTING.md.
Request-FP is available under the MIT License.