Dart 3.10 is taking off with dot shorthands, stable build hooks, nuanced deprecation annotations, and more!Learn more
Fetch data from the internet
- The basics of what HTTP requests and URIs are and what they are used for.
- Making HTTP requests using
package:http. - Decoding JSON strings into Dart objects with
dart:convert. - Converting JSON objects into class-based structures.
Most applications require some form of communication or data retrieval from the internet. Many apps do so through HTTP requests, which are sent from a client to a server to perform a specific action for a resource identified through aURI (Uniform Resource Identifier).
Data communicated over HTTP can technically be in any form, but usingJSON (JavaScript Object Notation) is a popular choice due to its human-readability and language independent nature. The Dart SDK and ecosystem also have extensive support for JSON with multiple options to best meet your app's requirements.
In this tutorial, you will learn more about HTTP requests, URIs, and JSON. Then you will learn how to usepackage:http as well as Dart's JSON support in thedart:convert library to fetch, decode, then use JSON-formatted data retrieved from an HTTP server.
Background concepts
#The following sections provide some extra background and information around the technologies and concepts used in the tutorial to facilitate fetching data from the server. To skip directly to the tutorial content, seeRetrieve the necessary dependencies.
JSON
#JSON (JavaScript Object Notation) is a data-interchange format that has become ubiquitous across application development and client-server communication. It is lightweight but also easy for humans to read and write due to being text based. With JSON, various data types and simple data structures such as lists and maps can be serialized and represented by strings.
Most languages have many implementations and parsers have become extremely fast, so you don't need to worry about interoperability or performance. For more information about the JSON format, seeIntroducing JSON. To learn more about working with JSON in Dart, see theUsing JSON guide.
Two other packages exist with platform-specific implementations for mobile.
- cronet_http provides access to the AndroidCronet HTTP client.
- cupertino_http provides access to Apple'sFoundation URL Loading System.
To learn more about their capabilities, consult the package documentation.
HTTP requests
# HTTP (Hypertext Transfer Protocol) is a stateless protocol designed for transmitting documents, originally between web clients and web servers. You interacted with the protocol to load this page, as your browser uses an HTTPGET request to retrieve the contents of a page from a web server. Since its introduction, use of the HTTP protocol and its various versions have expanded to applications outside the web as well, essentially wherever communication from a client to a server is needed.
HTTP requests sent from the client to communicate with the server are composed of multiple components. HTTP libraries, such aspackage:http, allow you to specify the following kinds of communication:
- An HTTP method defining the desired action, such as
GETto retrieve data orPOSTto submit new data. - The location of the resource through a URI.
- The version of HTTP being used.
- Headers that provide extra information to the server.
- An optional body, so the request can send data to the server, not just retrieve it.
To learn more about the HTTP protocol, check outAn overview of HTTP on the mdn web docs.
URIs and URLs
#To make an HTTP request, you need to provide aURI (Uniform Resource Identifier) to the resource. A URI is a character string that uniquely identifies a resource. A URL (Uniform Resource Locator) is a specific kind of URI that also provides the location of the resource. URLs for resources on the web contain three pieces of information. For this current page, the URL is composed of:
- The scheme used for determining the protocol used:
https - The authority or hostname of the server:
dart.dev - The path to the resource:
/tutorials/server/fetch-data.html
There are other optional parameters as well that aren't used by the current page:
- Parameters to customize extra behavior:
?key1=value1&key2=value2 - An anchor, that isn't sent to the server, which points to a specific location in the resource:
#uris
To learn more about URLs, seeWhat is a URL? on the mdn web docs.
Retrieve the necessary dependencies
# Thepackage:http library provides a cross-platform solution for making composable HTTP requests, with optional fine-grained control.
Avoid directly usingdart:io ordart:html to make HTTP requests. Those libraries are platform-dependent and tied to a single implementation.
To add a dependency onpackage:http, run the followingdart pub add command from the top of your repo:
$ dart pub add http To usepackage:http in your code, import it and optionallyspecify a library prefix:
import'package:http/http.dart'ashttp; To learn more specifics aboutpackage:http, see itspage on the pub.dev site and itsAPI documentation.
Build a URL
#As previously mentioned, to make an HTTP request, you first need a URL that identifies the resource being requested or endpoint being accessed.
In Dart, URLs are represented throughUri objects. There are many ways to build anUri, but due to its flexibility, parsing a string withUri.parse to create one is a common solution.
The following snippet shows two ways to create aUri object pointing to mock JSON-formatted information aboutpackage:http hosted on this site:
// Parse the entire URI, including the schemeUri.parse('https://dart.dev/f/packages/http.json');// Specifically create a URI with the https schemeUri.https('dart.dev','/f/packages/http.json'); To learn about other ways of building and interacting with URIs, see theURI documentation.
Make a network request
# If you just need to quickly fetch a string representation of a requested resource, you can use the top-levelread function found inpackage:http that returns aFuture<String> or throws aClientException if the request wasn't successful. The following example usesread to retrieve the mock JSON-formatted information aboutpackage:http as a string, then prints it out:
Many functions inpackage:http, includingread, access the network and perform potentially time-consuming operations, therefore they do so asynchronously and return aFuture. If you haven't encountered futures yet, you can learn about them—as well as theasync andawait keywords—in theasynchronous programming tutorial.
voidmain()async{finalhttpPackageUrl=Uri.https('dart.dev','/f/packages/http.json');finalhttpPackageInfo=awaithttp.read(httpPackageUrl);print(httpPackageInfo);} This results in the following JSON-formatted output, which can also be seen in your browser at/f/packages/http.json.
{"name":"http","latestVersion":"1.1.2","description":"A composable, multi-platform, Future-based API for HTTP requests.","publisher":"dart.dev","repository":"https://github.com/dart-lang/http"}Note the structure of the data (in this case a map), as you will need it when decoding the JSON later on.
If you need other information from the response, such as thestatus code or theheaders, you can instead use the top-levelget function that returns aFuture with aResponse.
The following snippet usesget to get the whole response in order to exit early if the request was not successful, which is indicated with a status code of200:
voidmain()async{finalhttpPackageUrl=Uri.https('dart.dev','/f/packages/http.json');finalhttpPackageResponse=awaithttp.get(httpPackageUrl);if(httpPackageResponse.statusCode!=200){print('Failed to retrieve the http package!');return;}print(httpPackageResponse.body);}There are many other status codes besides200 and your app might want to handle them differently. To learn more about what different status codes mean, seeHTTP response status codes on the mdn web docs.
Some server requests require more information, such as authentication or user-agent information; in this case you might need to includeHTTP headers. You can specify headers by passing in aMap<String, String> of the key-value pairs as theheaders optional named parameter:
awaithttp.get(Uri.https('dart.dev','/f/packages/http.json'),headers:{'User-Agent':'<product name>/<product-version>'},);Make multiple requests
# If you're making multiple requests to the same server, you can instead keep a persistent connection through aClient, which has similar methods to the top-level ones. Just make sure to clean up with theclose method when done:
voidmain()async{finalhttpPackageUrl=Uri.https('dart.dev','/f/packages/http.json');finalclient=http.Client();try{finalhttpPackageInfo=awaitclient.read(httpPackageUrl);print(httpPackageInfo);}finally{client.close();}} To enable the client to retry failed requests, importpackage:http/retry.dart and wrap your createdClient in aRetryClient:
import'package:http/http.dart'ashttp;import'package:http/retry.dart';voidmain()async{finalhttpPackageUrl=Uri.https('dart.dev','/f/packages/http.json');finalclient=RetryClient(http.Client());try{finalhttpPackageInfo=awaitclient.read(httpPackageUrl);print(httpPackageInfo);}finally{client.close();}} TheRetryClient has a default behavior for how many times to retry and how long to wait between each request, but its behavior can be modified through parameters to theRetryClient() orRetryClient.withDelays() constructors.
package:http has much more functionality and customization, so make sure to check out itspage on the pub.dev site and itsAPI documentation.
Decode the retrieved data
#While you now have made a network request and retrieved the returned data as string, accessing specific portions of information from a string can be a challenge.
Since the data is already in a JSON format, you can use Dart's built-injson.decode function in thedart:convert library to convert the raw string into a JSON representation using Dart objects. In this case, the JSON data is represented in a map structure and, in JSON, map keys are always strings, so you can cast the result ofjson.decode to aMap<String, dynamic>:
import'dart:convert';import'package:http/http.dart'ashttp;voidmain()async{finalhttpPackageUrl=Uri.https('dart.dev','/f/packages/http.json');finalhttpPackageInfo=awaithttp.read(httpPackageUrl);finalhttpPackageJson=json.decode(httpPackageInfo)asMap<String,dynamic>;print(httpPackageJson);}Create a structured class to store the data
#To provide the decoded JSON with more structure, making it easier to work with, create a class that can store the retrieved data using specific types depending on the schema of your data.
The following snippet shows a class-based representation that can store the package information returned from the mock JSON file you requested. This structure assumes all fields except therepository are required and provided every time.
classPackageInfo{finalStringname;finalStringlatestVersion;finalStringdescription;finalStringpublisher;finalUri?repository;PackageInfo({requiredthis.name,requiredthis.latestVersion,requiredthis.description,requiredthis.publisher,this.repository,});}Encode the data into your class
# Now that you have a class to store your data in, you need to add a mechanism to convert the decoded JSON into aPackageInfo object.
Convert the decoded JSON by manually writing afromJson method matching the earlier JSON format, casting types as necessary and handling the optionalrepository field:
classPackageInfo{// ···factoryPackageInfo.fromJson(Map<String,dynamic>json){finalrepository=json['repository']asString?;returnPackageInfo(name:json['name']asString,latestVersion:json['latestVersion']asString,description:json['description']asString,publisher:json['publisher']asString,repository:repository!=null?Uri.tryParse(repository):null,);}}A handwritten method, such as in the previous example, is often sufficient for relatively simple JSON structures, but there are more flexible options as well. To learn more about JSON serialization and deserialization, including automatic generation of the conversion logic, see theUsing JSON guide.
Convert the response to an object of your structured class
#Now you have a class to store your data and a way to convert the decoded JSON object into an object of that type. Next, you can write a function that pulls everything together:
- Create your
URIbased off a passed-in package name. - Use
http.getto retrieve the data for that package. - If the request didn't succeed, throw an
Exceptionor preferably your own customExceptionsubclass. - If the request succeeded, use
json.decodeto decode the response body into a JSON string. - Converted the decoded JSON string into a
PackageInfoobject using thePackageInfo.fromJsonfactory constructor you created.
Future<PackageInfo>getPackage(StringpackageName)async{finalpackageUrl=Uri.https('dart.dev','/f/packages/$packageName.json');finalpackageResponse=awaithttp.get(packageUrl);// If the request didn't succeed, throw an exceptionif(packageResponse.statusCode!=200){throwPackageRetrievalException(packageName:packageName,statusCode:packageResponse.statusCode,);}finalpackageJson=json.decode(packageResponse.body)asMap<String,dynamic>;returnPackageInfo.fromJson(packageJson);}classPackageRetrievalExceptionimplementsException{finalStringpackageName;finalint?statusCode;PackageRetrievalException({requiredthis.packageName,this.statusCode});}Utilize the converted data
#Now that you've retrieved data and converted it to a more easily accessible format, you can use it however you'd like. Some possibilities include outputting information to a CLI, or displaying it in aweb orFlutter app.
Here is complete, runnable example that requests, decodes, then displays the mock information about thehttp andpath packages:
import 'dart:convert';import 'package:http/http.dart' as http;void main() async { await printPackageInformation('http'); print(''); await printPackageInformation('path');}Future<void> printPackageInformation(String packageName) async { final PackageInfo packageInfo; try { packageInfo = await getPackage(packageName); } on PackageRetrievalException catch (e) { print(e); return; } print('Information about the $packageName package:'); print('Latest version: ${packageInfo.latestVersion}'); print('Description: ${packageInfo.description}'); print('Publisher: ${packageInfo.publisher}'); final repository = packageInfo.repository; if (repository != null) { print('Repository: $repository'); }}Future<PackageInfo> getPackage(String packageName) async { final packageUrl = Uri.https('dart.dev', '/f/packages/$packageName.json'); final packageResponse = await http.get(packageUrl); // If the request didn't succeed, throw an exception if (packageResponse.statusCode != 200) { throw PackageRetrievalException( packageName: packageName, statusCode: packageResponse.statusCode, ); } final packageJson = json.decode(packageResponse.body) as Map<String, dynamic>; return PackageInfo.fromJson(packageJson);}class PackageInfo { final String name; final String latestVersion; final String description; final String publisher; final Uri? repository; PackageInfo({ required this.name, required this.latestVersion, required this.description, required this.publisher, this.repository, }); factory PackageInfo.fromJson(Map<String, dynamic> json) { final repository = json['repository'] as String?; return PackageInfo( name: json['name'] as String, latestVersion: json['latestVersion'] as String, description: json['description'] as String, publisher: json['publisher'] as String, repository: repository != null ? Uri.tryParse(repository) : null, ); }}class PackageRetrievalException implements Exception { final String packageName; final int? statusCode; PackageRetrievalException({required this.packageName, this.statusCode}); @override String toString() { final buf = StringBuffer(); buf.write('Failed to retrieve package:$packageName information'); if (statusCode != null) { buf.write(' with a status code of $statusCode'); } buf.write('!'); return buf.toString(); }}For another example that covers fetching then displaying data in Flutter, see theFetching data from the internet Flutter recipe.
What next?
#Now that you have retrieved, parsed, and used data from the internet, consider learning more aboutConcurrency in Dart. If your data is large and complex, you can move retrieval and decoding to anotherisolate as a background worker to prevent your interface from becoming unresponsive.
Unless stated otherwise, the documentation on this site reflects Dart 3.10.0. Page last updated on 2025-10-18.View source orreport an issue.