One of the most efficient ways to add or update contacts using the v2 API is to use the Import Contacts multipart endpoint. It requires that you have a file in one of the supported formats with the contact information to be imported.
Why is using multipart bulk activity your best bet for adding and updating contacts?
There are 3 major elements to making this happen:
File types
The following file types are supported by the Import Contacts bulk activity endpoint:
The import file consists of columns, one for each contact property included, and rows, one for each contact being imported.
|
FIRST NAME |
LAST NAME |
HOME PHONE |
CITY |
US STATE/CA PROVINCE |
COUNTRY |
CUSTOM FIELD 1 |
contact@example.com |
John |
Doe |
1115555555 |
Des Moines |
IA |
US |
red |
contact2@example.com |
Rapunzel |
Longhair |
5555551111 |
Port Charlotte |
FL |
US |
green |
contact3@example.com |
Joaquin |
Arroyo |
2222222222 |
Montreal |
QC |
CA |
blue |
You can include the following contact properties in your import file. You must name the column containing the property as shown below:
|
|
There are two limitations that you need to be aware of:
The activity request will fail if the payload is greater than 4 MBs or if there are more 20,000 contacts.
Here are a few code examples using the Import Contacts multipart endpoint to import contacts from a file to a contact list.
Here’s an example CURL script that uploads the file contacts.txt to the import contacts multipart endpoint, adding the contacts to listId=5. It assumes that the data file is located in the same directory from which the script is run, otherwise you must use an absolute path to the data file.
curl -i -H "Authorization: Bearer <Oauth2.0_access_token>" -H "Content-Type: multipart/form-data" -H "Accept: application/json" -X POST -F 'lists=5' -F 'file_name=contacts.txt' -F 'data=@contacts.txt' "https://api.constantcontact.com/v2/activities/addcontacts?api_key=<api_key>"
Here's an example of a multipart post written in Java that imports the file contacts.txt, adding the contacts to list 1 (listId=1).
final HttpClient httpclient = new DefaultHttpClient();
final HttpPost httppost = new HttpPost("https://api.constantcontact.com/v2/activities/addcontacts?api_key=<api_key>");
httppost.addHeader("Authorization", "Bearer <OAuth2.0_access_token>");
httppost.addHeader("Accept", ”application/json”);
httppost.addHeader("Content-Type", "multipart/form-data");
final File fileToUse = new File("/path_to_file/contacts.txt"); //e.g. /temp/contact.txt
final FileBody data = new FileBody(fileToUse);
final String listIds = "1";
final StringBody lists = new StringBody(listIds);
final StringBody fileName = new StringBody(fileToUse.getName());
final MultipartEntity reqEntity = new MultipartEntity();
reqEntity.addPart("file_name", fileName);
reqEntity.addPart("lists", lists);
reqEntity.addPart("data", data);
httppost.setEntity(reqEntity);
final HttpResponse response = httpclient.execute(httppost);
final HttpEntity resEntity = response.getEntity();
EntityUtils.consume(resEntity);
httpclient.getConnectionManager().shutdown();
}
The Constant Contact Java SDK includes methods for all bulk activities, including the import contacts multipart bulk activity endpoint. Here’s a snippet of code that uses the ctct.addBulkContactsMultipart Java SDK method to import the contacts in file.xlsx and add them to contact lists 4 and 5.
System.out.println("Multipart test...");
File f = new File("file.xlsx");
ArrayList<String> listIds = new ArrayList<String>();
listIds.add("4"); listIds.add("5");
try { ContactsResponse response1 =
ctct.addBulkContactsMultipart("file.xlsx", f, listIds);
System.out.println(response1.toJSON()); } catch
(ConstantContactServiceException e) { List<CUrlRequestError> errors =
e.getErrorInfo(); for (CUrlRequestError error : errors) {
System.out.println(error.getErrorMessage()); } }
This example coded in Ruby is available with all supporting files in our Ruby SDK.
#
# myapp.rb
# ConstantContact
#
# Copyright (c) 2013 Constant Contact. All rights reserved.
require 'rubygems'
require 'sinatra'
require 'active_support'
require 'yaml'
require 'constantcontact'
# This is a Sinatra application (http://www.sinatrarb.com/).
# Update config.yml with your data before running the application.
# Run this application like this : ruby myapp.rb
# Name this action according to your
# Constant Contact API Redirect URL, see config.yml
get '/cc_callback' do
cnf = YAML::load(File.open('config/config.yml'))
@oauth = ConstantContact::Auth::OAuth2.new(
:api_key => cnf['api_key'],
:api_secret => cnf['api_secret'],
:redirect_url => cnf['redirect_url']
)
@error = params[:error]
@user = params[:username]
@code = params[:code]
if @code
begin
@lists = []
response = @oauth.get_access_token(@code)
@token = response['access_token']
cc = ConstantContact::Api.new(cnf['api_key'])
lists = cc.get_lists(@token)
if lists
lists.each do |list|
# Select the first list, by default
selected = list == lists.first
@lists << {
'id' => list.id,
'name' => list.name,
'selected' => selected
}
end
end
rescue => e
message = parse_exception(e)
@error = "An error occured when saving the contacts : " + message
end
erb :contacts_multipart
else
erb :callback
end
end
# Name this action according to your
# Constant Contact API Redirect URL, see config.yml
post '/cc_callback' do
cnf = YAML::load(File.open('config/config.yml'))
@error = params[:error]
@user = params[:username]
@code = params[:code]
@token = params[:token]
if @code
cc = ConstantContact::Api.new(cnf['api_key'])
@activity = params[:activity]
lists = params[:lists] || {}
lists['checkboxes'] = [] if lists['checkboxes'].blank?
@lists = []
if lists['ids']
lists['ids'].each do |key, list_id|
list_name = lists['names'][key]
selected = !(lists['checkboxes'].blank? || lists['checkboxes'][key].blank?)
@lists << {
'id' => list_id,
'name' => list_name,
'selected' => selected
}
end
end
begin
if @activity
# Validate
raise 'Please select a file' if @activity['file'].blank?
file_name = @activity['file'][:filename]
contents = @activity['file'][:tempfile].read
add_to_lists = []
lists['ids'].each do |key, list_id|
add_to_lists << list_id if lists['checkboxes'][key]
end
add_to_lists = add_to_lists.join(',')
if /remove_contacts/.match(file_name)
cc.add_remove_contacts_from_lists_activity_from_file(@token, file_name, contents, add_to_lists)
elsif /add_contacts/.match(file_name)
cc.add_create_contacts_activity_from_file(@token, file_name, contents, add_to_lists)
end
redirect '/cc_callback'
end
rescue => e
message = parse_exception(e)
@error = "An error occured when saving the contacts : " + message
#puts e.backtrace
end
erb :contacts_multipart
else
erb :callback
end
end
def parse_exception(e)
if e.respond_to?(:response)
hash_error = JSON.parse(e.response)
message = hash_error.first['error_message']
else
message = e.message
end
message.to_s
end
Hi Rich,
Thanks for providing details for multipart imports.
One question: the Java sample code provided does not work for me. Could you please elaborate on what parts of the Java code I must modify myself? I've personalized the obvious ones like API key, authorization token, etc., but I feel that I could be missing something.
The code I used is:
{
final HttpClient httpclient = new DefaultHttpClient();
final HttpPost httppost = new HttpPost("https://api.constantcontact.com/v2/activities/addcontacts?api_key=<I put my API key>");
httppost.addHeader("Authorization", "Bearer <I put my access token here>");
httppost.addHeader("Accept", "application/json”);
httppost.addHeader("content-type", "multipart/form-data");
final File fileToUse = new File("C:\Users\c16196\Documents\Work\Projects\APIConstantContact\Test File POST Call.xlsx");
final FileBody data = new FileBody(fileToUse);
final String listIds = "1";
final StringBody lists = new StringBody(listIds);
final StringBody fileName = new StringBody(fileToUse.getName());
final MultipartEntity reqEntity = new MultipartEntity();
reqEntity.addPart("file_name", fileName);
reqEntity.addPart("lists", lists);
reqEntity.addPart("data", data);
httppost.setEntity(reqEntity);
final HttpResponse response = httpclient.execute(httppost);
final HttpEntity resEntity = response.getEntity();
EntityUtils.consume(resEntity);
httpclient.getConnectionManager().shutdown();
}
The response I received is:
[{
"error_key": "http.header.content_type.invalid",
"error_message": "Invalid content type. API only supports application/json."
}]
Any help you can offer helps me.
Thank you,
Nick W
Hi Nick,
Try changing
httppost.addHeader("content-type", "multipart/form-data");
to
httppost.addHeader("Content-Type", "multipart/form-data");
I apologize if the sample is incorrect, and I will update it if this indeed fixes the issue.
In the meantime, we will test the sample code again, and see if we run into any issues as you have.
Regards,
Richard,
Thank you for the reply, and sorry for my late response.
Capitalizing the 'C' didn't work, but I just realized that this may be a simple firewall issue on my side (I have yet to whitelist api.constantcontact.com). I'll whitelist the domain, try it again, and reply back if it still doesn't work.
Thanks,
Nick
I got that same response.. I am wonder what really goes into the data field. Since we are uploading a file, just questioning what goes there. Seems I am close.
<cfhttp url="https://api.constantcontact.com/v2/activities/addcontacts?api_key=" method="post" result="cc_results"
file="C:\Intranet\filelocation\load.xlsx"
username="username"
password="password">
<cfhttpparam type="header" name="content-type" value="multipart/form-data">
<cfhttpparam type="header" name="accept" value="application/json">
<cfhttpparam type="header" name="Authorization" value="Bearer code">
<cfhttpparam type="formField" name="lists" value="listid">
<cfhttpparam type="formField" name="data" value="First Name,Last Name,EMAIL"> ?????
<cfhttpparam type="formField" name="file_name" value="load.xlsx"> ?????
<cfhttpparam type="formField" name="activityType" value="ADD_CONTACTS">
</cfhttp>
now I get:
Filecontent | org.apache.cxf.interceptor.Fault: Couldn't determine the boundary from the message! |
Header | HTTP/1.1 500 Server Error Cache-Control: private, no-cache, no-store, max-age=0, must-revalidate, no-cache="Set-Cookie" Content-Type: application/json Date: Wed, 19 Nov 2014 18:22:25 GMT Pragma: no-cache Server: Apache Vary: Accept-Encoding,User-Agent X-Mashery-Responder: prod-j-worker-us-east-1b-55.mashery.com X-Powered-By: Content-Length: 83 Connection: Close |
Hello,
Based on what you are sending and the error response, there are a couple of things happening:
For the first part, there is an important distinction that with a multipart POST you are construction a POST body that uses boundaries to separate the individual items in the POST, rather than appending them to the URL as this code appears to be doing. This is very important because the multipart request needs to include binary data. Unfortunately I am not particularly skilled with ColdFusion and am not aware of what the best method for a proper multipart request in ColdFusion is.
For the second part, the information that should be sent is as follows:
data - Binary contents of the file being uploaded
file_name - Name of the file being uploaded
lists - Comma separated set of list IDs
activityType - This does NOT need to be sent. In the V2 API, the URL determines the activity type.
Hopefully this information is enough to get things moving again. If you continue to have trouble, please let us know!
Best Regards,
The holidays have come and gone. For many seasonal businesses, this means the rush of shoppers has decreased as well. Instead of turning off the lights and waiting for spring, make your email marketi...
See Article