Fuel
The core package for Fuel. The documentation outlined here touches most subjects and functions but is not exhaustive.

Installation

You can download and install Fuel with Maven and Gradle. The core package has the following dependencies:
1
implementation 'com.github.kittinunf.fuel:fuel:<latest-version>'
Copied!

Usage

Making Requests

You can make requests using functions on Fuel, a FuelManager instance or the string extensions.
1
Fuel.get("https://httpbin.org/get")
2
.response { request, response, result ->
3
println(request)
4
println(response)
5
val (bytes, error) = result
6
if (bytes != null) {
7
println("[response bytes] ${String(bytes)}")
8
}
9
}
10
11
/*
12
* --> GET https://httpbin.org/get
13
* "Body : (empty)"
14
* "Headers : (0)"
15
*
16
17
* <-- 200 (https://httpbin.org/get)
18
* Response : OK
19
* Length : 268
20
* Body : ({
21
* "args": {},
22
* "headers": {
23
* "Accept": "text/html, image/gif, image/jpeg, *; q=.2, *\/*; q=.2",
24
* "Connection": "close",
25
* "Host": "httpbin.org",
26
* "User-Agent": "Java/1.8.0_172"
27
* },
28
* "origin": "123.456.789.123",
29
* "url": "https://httpbin.org/get"
30
* })
31
* Headers : (8)
32
* Connection : keep-alive
33
* Date : Thu, 15 Nov 2018 00:47:50 GMT
34
* Access-Control-Allow-Origin : *
35
* Server : gunicorn/19.9.0
36
* Content-Type : application/json
37
* Content-Length : 268
38
* Access-Control-Allow-Credentials : true
39
* Via : 1.1 vegur
40
41
* [response bytes] {
42
* "args": {},
43
* "headers": {
44
* "Accept": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2",
45
* "Connection": "close",
46
* "Host": "httpbin.org",
47
* "User-Agent": "Java/1.8.0_172"
48
* },
49
* "origin": "123.456.789.123",
50
* "url": "https://httpbin.org/get"
51
* }
52
*/
Copied!
The extensions and functions made available by the core package are listed here:
Fuel Method
String extension
Fuel/FuelManager method
Method.GET
"https://httpbin.org/get".httpGet()
Fuel.get("https://httpbin.org/get")
Method.POST
"https://httpbin.org/post".httpPost()
Fuel.post("https://httpbin.org/post")
Method.PUT
"https://httpbin.org/put".httpPut()
Fuel.put("https://httpbin.org/put")
Method.PATCH
"https://httpbin.org/patch".httpPatch()
Fuel.patch("https://httpbin.org/patch")
Method.HEAD
"https://httpbin.org/get".httpHead()
Fuel.head("https://httpbin.org/get")
Method.OPTIONS
not supported
Fuel.request(Method.OPTIONS, "https://httpbin.org/anything")
Method.TRACE
not supported
Fuel.request(Method.TRACE, "https://httpbin.org/anything")
Method.CONNECT
not supported
not supported

About PATCH requests

The default client is HttpClient which is a thin wrapper over java.net.HttpUrlConnection. java.net.HttpUrlConnection does not support a [PATCH](https://download.java.net/jdk7/archive/b123/docs/api/java/net/HttpURLConnection.html#setRequestMethod(java.lang.String)) method. HttpClient converts PATCH requests to a POST request and adds a X-HTTP-Method-Override: PATCH header. While this is a semi-standard industry practice not all APIs are configured to accept this header by default.
1
Fuel.patch("https://httpbin.org/patch")
2
.also { println(it) }
3
.response { result -> }
4
5
/* --> PATCH https://httpbin.org/post
6
* "Body : (empty)"
7
* "Headers : (1)"
8
* Content-Type : application/x-www-form-urlencoded
9
*/
10
11
// What is actually sent to the server
12
13
/* --> POST (https://httpbin.org/post)
14
* "Body" : (empty)
15
* "Headers : (3)"
16
* Accept-Encoding : compress;q=0.5, gzip;q=1.0
17
* Content-Type : application/x-www-form-urlencoded
18
* X-HTTP-Method-Override : PATCH
19
*/
Copied!
Experimental
As of version 1.16.x you can opt-in to forcing a HTTP Method on the java.net.HttpUrlConnnection instance using reflection.
1
FuelManager.instance.forceMethods = true
2
3
Fuel.patch("https://httpbin.org/patch")
4
.also { println(it) }
5
.response { result -> }
6
7
/* --> PATCH (https://httpbin.org/patch)
8
* "Body" : (empty)
9
* "Headers : (3)"
10
* Accept-Encoding : compress;q=0.5, gzip;q=1.0
11
* Content-Type : application/x-www-form-urlencoded
12
*/
Copied!

About CONNECT request

Connect is not supported by the Java JVM via the regular HTTP clients, and is therefore not supported.

Adding Parameters

All the String extensions listed above, as well as the Fuel and FuelManager calls accept a parameter parameters: Parameters.
    URL encoded style for GET and DELETE request
    1
    Fuel.get("https://httpbin.org/get", listOf("foo" to "foo", "bar" to "bar"))
    2
    .url
    3
    // https://httpbin.org/get?foo=foo&bar=bar
    Copied!
    1
    Fuel.delete("https://httpbin.org/delete", listOf("foo" to "foo", "bar" to "bar"))
    2
    .url
    3
    // https://httpbin.org/delete?foo=foo&bar=bar
    Copied!
    Support x-www-form-urlencoded for PUT, POST and PATCH
    1
    Fuel.post("https://httpbin.org/post", listOf("foo" to "foo", "bar" to "bar"))
    2
    .also { println(it.url) }
    3
    .also { println(String(it.body().toByteArray())) }
    4
    5
    // https://httpbin.org/post
    6
    // "foo=foo&bar=bar"
    Copied!
    1
    Fuel.put("https://httpbin.org/put", listOf("foo" to "foo", "bar" to "bar"))
    2
    .also { println(it.url) }
    3
    .also { println(String(it.body().toByteArray())) }
    4
    5
    // https://httpbin.org/put
    6
    // "foo=foo&bar=bar"
    Copied!

Parameters with Body

If a request already has a body, the parameters are url-encoded instead. You can remove the handling of parameter encoding by removing the default ParameterEncoder request interceptor from your FuelManager.

Parameters with multipart/form-data

The UploadRequest handles encoding parameters in the body. Therefore by default, parameter encoding is ignored by ParameterEncoder if the content type is multipart/form-data.

Parameters with empty, array, list or null values

All requests can have parameters, regardless of the method.
    a list is encoded as key[]=value1&key[]=value2&...
    an array is encoded as key[]=value1&key[]=value2&...
    an empty string value is encoded as key
    a null value is removed

Adding Request body

Bodies are formed from generic streams, but there are helpers to set it from values that can be turned into streams. It is important to know that, by default, the streams are NOT read into memory until the Request is sent. However, if you pass in an in-memory value such as a ByteArray or String, Fuel uses RepeatableBody, which are kept into memory until the Request is dereferenced.
When you're using the default Client, bodies are supported for:
    POST
    PUT
    PATCH (actually a POST, as noted above)
    DELETE
There are several functions to set a Body for the request. If you are looking for a multipart/form-data upload request, checkout the UploadRequest feature.
1
Fuel.post("https://httpbin.org/post")
2
.body("My Post Body")
3
.also { println(it) }
4
.response { result -> }
5
6
/* --> POST https://httpbin.org/post
7
* "Body : My Post Body"
8
* "Headers : (1)"
9
* Content-Type : application/x-www-form-urlencoded
10
*/
Copied!

Use application/json

If you don't want to set the application/json header, you can use .jsonBody(value: String) extension to automatically do this for you.
1
Fuel.post("https://httpbin.org/post")
2
.jsonBody("{ \"foo\" : \"bar\" }")
3
.also { println(it) }
4
.response { result -> }
5
6
/* --> POST https://httpbin.org/post
7
* "Body : { "foo" : "bar" }"
8
* "Headers : (1)"
9
* Content-Type : application/json
10
*/
Copied!

from String

1
Fuel.post("https://httpbin.org/post")
2
.header(Headers.CONTENT_TYPE, "text/plain")
3
.body("my body is plain")
4
.also { println(it) }
5
.response { result -> }
6
7
/* --> POST https://httpbin.org/post
8
* "Body : my body is plain"
9
* "Headers : (1)"
10
* Content-Type : text/plain
11
*/
Copied!

from a File

1
Fuel.post("https://httpbin.org/post")
2
.header(Headers.CONTENT_TYPE, "text/plain")
3
.body(File("lipsum.txt"))
4
.also { println(it) }
5
.response { result -> }
6
7
/* --> POST https://httpbin.org/post
8
* "Body : Lorem ipsum dolor sit amet, consectetur adipiscing elit."
9
* "Headers : (1)"
10
* Content-Type : text/plain
11
*/
Copied!

from a InputStream

1
val stream = ByteArrayInputStream("source-string-from-string".toByteArray())
2
3
Fuel.post("https://httpbin.org/post")
4
.header(Headers.CONTENT_TYPE, "text/plain")
5
.body(stream)
6
.also { println(it) }
7
.response { result -> }
8
9
/* --> POST https://httpbin.org/post
10
* "Body : source-string-from-string"
11
* "Headers : (1)"
12
* Content-Type : text/plain
13
*/
Copied!

from a lazy source (InputStream)

Fuel always reads the body lazily, which means you can also provide a callback that will return a stream. This is also known as a BodyCallback:
1
val produceStream = { ByteArrayInputStream("source-string-from-string".toByteArray()) }
2
3
Fuel.post("https://httpbin.org/post")
4
.header(Headers.CONTENT_TYPE, "text/plain")
5
.body(produceStream)
6
.also { println(it) }
7
.response { result -> }
8
9
/* --> POST https://httpbin.org/post
10
* "Body : source-string-from-string"
11
* "Headers : (1)"
12
* Content-Type : text/plain
13
*/
Copied!

Using automatic body redirection

The default redirection interceptor only forwards RepeatableBody, and only if the status code is 307 or 308, as per the RFCs. In order to use a RepeatableBody, pass in a String or ByteArray as body, or explicitely set repeatable = true for the fun body(...) call.
NOTE this loads the entire body into memory, and therefore is not suited for large bodies.

Adding Headers

There are many ways to set, overwrite, remove and append headers. For your convenience, internally used and common header names are attached to the Headers companion and can be accessed (e.g. Headers.CONTENT_TYPE, Headers.ACCEPT, ...).
The most common ones are mentioned here:

Reading HeaderValues

call
arguments
action
request[header]
header: String
Get the current values of the header, after normalisation of the header
request.header(header)
header: String
Get the current values

(Over)writing HeaderValues

call
arguments
action
request[header] = values
header: String, values: Collection<*>
Set the values of the header, overriding what's there, after normalisation of the header
request[header] = value
header: String, value: Any
Set the value of the header, overriding what's there, after normalisation of the header
request.header(map)
map: Map<String, Any>
Replace the headers with the map provided
request.header(pair, pair, ...)
vararg pairs: Pair<String, Any>
Replace the headers with the pairs provided
request.header(header, values)
header: String, values: Collection<*>
Replace the header with the provided values
request.header(header, value)
header: String, value: Any
Replace the header with the provided value
request.header(header, value, value, ...)
header: String, vararg values: Any
Replace the header with the provided values

Appending HeaderValues

call
arguments
action
request.appendHeader(pair, pair, ...)
vararg pairs: Pair<String, Any>
Append each pair, using the key as header name and value as header content
request.appendHeader(header, value)
header: String, value: Any
Appends the value to the header or sets it if there was none yet
request.appendHeader(header, value, value, ...)
header: String, vararg values: Any
Appends the value to the header or sets it if there was none yet
Note that headers which by the RFC may only have one value are always overwritten, such as Content-Type.

FuelManager base headers vs. Request headers

The baseHeaders set through a FuelManager are only applied to a Request if that request does not have that specific header set yet. There is no appending logic. If you set a header it will overwrite the base value.

Client headers vs. Request headers

Any Client can add, remove or transform HeaderValues before it sends the Request or after it receives the Response. The default Client for example sets TE values.

HeaderValues values are List

Even though some headers can only be set once (and will overwrite even when you try to append), the internal structure is always a list. Before a Request is made, the default Client collapses the multiple values, if allowed by the RFCs, into a single header value delimited by a separator for that header. Headers that can only be set once will use the last value by default and ignore earlier set values.

Adding Authentication

Authentication can be added to a Request using the .authentication() feature. By default, authentication is passed on when using the default redirectResponseInterceptor (which is enabled by default), unless it is redirecting to a different host. You can remove this behaviour by implementing your own redirection logic.
When you call .authentication(), a few extra functions are available. If you call a regular function (e.g. .header()) the extra functions are no longer available, but you can safely call .authentication() again without losing any previous calls.
    Basic authentication
1
val username = "username"
2
val password = "abcd1234"
3
4
Fuel.get("https://httpbin.org/basic-auth/$user/$password")
5
.authentication()
6
.basic(username, password)
7
.response { result -> }
Copied!
    Bearer authentication
1
val token = "mytoken"
2
3
Fuel.get("https://httpbin.org/bearer")
4
.authentication()
5
.bearer(token)
6
.response { result -> }
Copied!
    Any authentication using a header
1
Fuel.get("https://httpbin.org/anything")
2
.header(Headers.AUTHORIZATION, "Custom secret")
3
.response { result -> }
Copied!

Adding Progress callbacks

Any request supports Progress callbacks when uploading or downloading a body; the Connection header does not support progress (which is the only thing that is sent if there are no bodies). You can have as many progress handlers of each type as you like.

Request progress

1
Fuel.post("/post")
2
.body(/*...*/)
3
.requestProgress { readBytes, totalBytes ->
4
val progress = readBytes.toFloat() / totalBytes.toFloat() * 100
5
println("Bytes uploaded $readBytes / $totalBytes ($progress %)")
6
}
7
.response { result -> }
Copied!

Response progress

1
Fuel.get("/get")
2
.responseProgress { readBytes, totalBytes ->
3
val progress = readBytes.toFloat() / totalBytes.toFloat() * 100
4
println("Bytes downloaded $readBytes / $totalBytes ($progress %)")
5
}
6
.response { result -> }
Copied!

Throttling progress output

Often the progress of a download will be shown as notification. Since Android Nougat (API 24), only 10 notification updates per second per app are allowed. Anything more than that will result in a log error like E/NotificationService: Package enqueue rate is 10.062265. Shedding events. package=....
It is the responsibility of the library user – not of fuel – to remedy this limit. A simple solution could look like this:
1
var lastUpdate = 0L
2
Fuel.get("/get")
3
.progress { readBytes, totalBytes ->
4
// allow 2 updates/second max - more than 10/second will be blocked
5
if (System.currentTimeMillis() - lastUpdate > 500) {
6
lastUpdate = System.currentTimeMillis()
7
val progress = readBytes.toFloat() / totalBytes.toFloat() * 100
8
myNotificationHelper.notifyDownloadProgress(progress)
9
}
10
}
11
.response { result -> }
Copied!

Why does totalBytes increase?

Not all source Body or Response Body report their total size. If the size is not known, the current size will be reported. This means that you will constantly get an increasing amount of totalBytes that equals readBytes.

Using multipart/form-data (UploadRequest)

Fuel supports multipart uploads using the .upload() feature. You can turn any Request into a upload request by calling .upload() or call .upload(method = Method.POST) directly onto Fuel / FuelManager.
When you call .upload(), a few extra functions are available. If you call a regular function (e.g. .header()) the extra functions are no longer available, but you can safely call .upload() again without losing any previous calls.
method
arguments
action
request.add { }
varargs dataparts: (Request) -> DataPart
Add one or multiple DataParts lazily
request.add()
varargs dataparts: DataPart
Add one or multiple DataParts
request.progress(handler)
hander: ProgressCallback
Add a requestProgress handler
1
Fuel.upload("/post")
2
.add { FileDataPart(File("myfile.json"), name = "fieldname", filename="contents.json") }
3
.response { result -> }
Copied!

DataPart from File

In order to add DataParts that are sources from a File, you can use FileDataPart, which takes a file: File. There are some sane defaults for the field name name: String, and remote file name filename: String, as well as the Content-Type and Content-Disposition fields, but you can override them.
In order to receive a list of files, for example in the field files, use the array notation:
1
Fuel.upload("/post")
2
.add(
3
FileDataPart(File("myfile.json"), name = "files[]", filename="contents.json"),
4
FileDataPart(File("myfile2.json"), name = "files[]", filename="contents2.json"),
5
FileDataPart(File("myfile3.json"), name = "files[]", filename="contents3.json")
6
)
7
.response { result -> }
Copied!
Sending multiple files in a single datapart is not supported as it's deprecated by the multipart/form-data RFCs, but to simulate this behaviour, give the same name to multiple parts.
You can use the convenience constructors FileDataPart.from(directory: , filename: , ...args) to create a FileDataPart from String arguments.

DataPart from inline content

Sometimes you have some content inline that you want to turn into a DataPart. You can do this with InlineDataPart:
1
Fuel.upload("/post")
2
.add(
3
FileDataPart(File("myfile.json"), name = "file", filename="contents.json"),
4
InlineDataPart(myInlineContent, name = "metadata", filename="metadata.json", contentType = "application/json")
5
)
6
.response { result -> }
Copied!
A filename is not mandatory and is empty by default; the contentType is text/plain by default.

DataPart from InputStream (formely Blob)

You can also add dataparts from arbitrary InputStreams, which you can do using BlobDataPart:
1
Fuel.upload("/post")
2
.add(
3
FileDataPart(File("myfile.json"), name = "file", filename="contents.json"),
4
BlobDataPart(someInputStream, name = "metadata", filename="metadata.json", contentType = "application/json", contentLength = 555)
5
)
6
.response { result -> }
Copied!
If you don't set the contentLength to a positive integer, your entire Request Content-Length will be undeterminable and the default HttpClient will switch to chunked streaming mode with an arbitrary stream buffer size.

Multipart request without a file

Simply don't call add. The parameters are encoded as parts!
1
val formData = listOf("Email" to "[email protected]", "Name" to "Joe Smith" )
2
3
Fuel.upload("/post", param = formData)
4
.response { result -> }
Copied!

Getting a Response

As mentioned before, you can use Fuel both synchronously and a-synchronously, with support for coroutines.

Blocking responses

By default, there are three response functions to get a request synchronously:
function
arguments
result
response()
none
ResponseResultOf<ByteArray>
responseString(charset)
charset: Charset
ResponseResultOf<String>
responseObject(deserializer)
deserializer: Deserializer<U>
ResponseResultOf<U>
The default charset is UTF-8. If you want to implement your own deserializers, scroll down to advanced usage.

Async responses

Add a handler to a blocking function, to make it asynchronous:
function
arguments
result
response() { handler }
handler: Handler
CancellableRequest
responseString(charset) { handler }
charset: Charset, handler: Handler
CancellableRequest
responseObject(deserializer) { handler }
deserializer: Deserializer, handler: Handler
CancellableRequest
The default charset is UTF-8. If you want to implement your own deserializers, scroll down to advanced usage.

Suspended responses

The core package has limited support for coroutines:
function
arguments
result
await(deserializer)
deserializer: Deserializer<U>
U
awaitResult(deserializer)
deserializer: Deserializer<U>
Result<U, FuelError>
awaitResponse(deserializer)
deserializer: Deserializer<U>
ResponseOf<U>
awaitResponseResult(deserializer)
deserializer: Deserializer<U>
ResponseResultOf<U>
When using other packages such as fuel-coroutines, more response/await functions are available.

Response types

    The ResponseResultOf<U> type is a Triple of the Request, Response and a Result<U, FuelError>
    The ResponseOf<U> type is a Triple of the Request, Response and a U; errors are thrown
    The Result<U, FuelError> type is a non-throwing wrapper around U
    The U type doesn't wrap anything; errors are thrown

Handler types

When defining a handler, you can use one of the following for all responseXXX functions that accept a Handler:
type
handler fns
arguments
description
Handler<T>
2
1
calls success with an instance of T or failure on errors
ResponseHandler<T>
2
3
calls success with Request, Response and an instance of T, or failure or errors
ResultHandler<T>
1
1
invokes the function with Result<T, FuelError>
ResponseResultHandler<T>
1
3
invokes the function with Request Response and Result<T, FuelError>
This means that you can either choose to unwrap the Result yourself using a ResultHandler or ResponseResultHandler, or define dedicated callbacks in case of success or failure.

Dealing with Result<T, FuelError>

Result is a functional style data structure that represents data that contains result of Success or Failure but not both. It represents the result of an action that can be success (with result) or error.
Working with result is easy:
    You can call [fold] and define a tranformation function for both cases that results in the same return type,
    [destructure] as (data, error) = result because it is just a data class or
    use when checking whether it is Result.Success or Result.Failure

Download response to output (File or OutputStream)

Fuel supports downloading the request Body to a file using the .download() feature. You can turn any Request into a download request by calling .download() or call .download(method = Method.GET) directly onto Fuel / FuelManager.
When you call .download(), a few extra functions are available. If you call a regular function (e.g. .header()) the extra functions are no longer available, but you can safely call .download() again without losing any previous calls.
method
arguments
action
request.fileDestination { }
(Response, Request) -> File
Set the destination file callback where to store the data
request.streamDestination { }
(Response, Request) -> Pair<OutputStream, () -> InputStream>
Set the destination file callback where to store the data
request.progress(handler)
hander: ProgressCallback
Add a responseProgress handler
1
Fuel.download("https://httpbin.org/bytes/32768")
2
.fileDestination { response, url -> File.createTempFile("temp", ".tmp") }
3
.progress { readBytes, totalBytes ->
4
val progress = readBytes.toFloat() / totalBytes.toFloat() * 100
5
println("Bytes downloaded $readBytes / $totalBytes ($progress %)")
6
}
7
.response { result -> }
Copied!
The stream variant expects your callback to provide a Pair with both the OutputStream to write too, as well as a callback that gives an InputStream, or raises an error.
    The OutputStream is always closed after the body has been written. Make sure you wrap whatever functionality you need on top of the stream and don't rely on the stream to remain open.
    The () -> InputStream replaces the body after the current body has been written to the OutputStream. It is used to make sure you can also retrieve the body via the response / await method results. If you don't want the body to be readable after downloading it, you have to do two things:
      use an EmptyDeserializer with await(deserializer) or one of the response(deserializer) variants
      provide an InputStream callback that throws or returns an empty InputStream.

Cancel an async Request

The response functions called with a handler are async and return a CancellableRequest. These requests expose a few extra functions that can be used to control the Future that should resolve a response:
1
val request = Fuel.get("https://httpbin.org/get")
2
.interrupt { request -> println("${request.url} was interrupted and cancelled") }
3
.response { result ->
4
// if request is cancelled successfully, response callback will not be called.
5
// Interrupt callback (if provided) will be called instead
6
}
7
8
request.cancel() // this will cancel on-going request
Copied!
If you can't get hold of the CancellableRequest because, for example, you are adding this logic in an Interceptor, a generic Queue, or a ProgressCallback, you can call tryCancel() which returns true if it was cancelled and false otherwise. At this moment blocking requests can not be cancelled.

Advanced Configuration

Request Configuration

    baseHeaders is to manage common HTTP header pairs in format of Map<String, String>.
      The base headers are only applied if the request does not have those headers set.
1
FuelManager.instance.baseHeaders = mapOf("Device" to "Android")
Copied!
    Headers can be added to a request via various methods including
1
fun header(name: String, value: Any): Request = request.header("foo", "a")
2
fun header(pairs: Map<String, Any>): Request = request.header(mapOf("foo" to "a"))
3
fun header(vararg pairs: Pair<String, Any>): Request = request.header("foo" to "a")
4
5
operator fun set(header: String, value: Collection<Any>): Request = request["foo"] = listOf("a", "b")
6
operator fun set(header: String, value: Any): Request = request["foo"] = "a"
Copied!
    By default, all subsequent calls overwrite earlier calls, but you may use the appendHeader variant to append values to existing values.
      In earlier versions (1.x.y), a mapOf overwrote, and varargs pair did not, but this was confusing. In 2.0, this issue has been fixed and improved so it works as expected.
1
fun appendHeader(header: String, value: Any): Request
2
fun appendHeader(header: String, vararg values: Any): Request
3
fun appendHeader(vararg pairs: Pair<String, Any>): Request
Copied!
    Some of the HTTP headers are defined under Headers.Companion and can be used instead of literal strings. This is an encouraged way to configure your header in 2.x.y.
1
Fuel.post("/my-post-path")
2
.header(Headers.ACCEPT, "text/html, */*; q=0.1")
3
.header(Headers.CONTENT_TYPE, "image/png")
4
.header(Headers.COOKIE to "basic=very")
5
.appendHeader(Headers.COOKIE to "value_1=foo", Headers.COOKIE to "value_2=bar", Headers.ACCEPT to "application/json")