diff --git a/vipra-backend/src/main/java/de/vipra/rest/model/ResponseWrapper.java b/vipra-backend/src/main/java/de/vipra/rest/model/ResponseWrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..c7642be81068e8569301c1e8ba979bf92d765598
--- /dev/null
+++ b/vipra-backend/src/main/java/de/vipra/rest/model/ResponseWrapper.java
@@ -0,0 +1,102 @@
+package de.vipra.rest.model;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.core.Response.Status;
+
+import de.vipra.util.StringUtils;
+
+public class ResponseWrapper<T> {
+
+	private List<APIError> errors;
+	private Map<String, Object> headers;
+
+	public List<APIError> getErrors() {
+		return errors;
+	}
+
+	public void addError(APIError error) {
+		if (errors == null)
+			errors = new ArrayList<>();
+		errors.add(error);
+	}
+
+	public void addHeader(String name, Object value) {
+		if (headers == null)
+			headers = new HashMap<>();
+		headers.put("V-" + StringUtils.capitalize(name), value);
+	}
+
+	public boolean hasErrors() {
+		return errors != null && errors.size() > 0;
+	}
+
+	private void addHeaders(ResponseBuilder builder) {
+		if (headers == null || headers.size() == 0)
+			return;
+		for (Entry<String, Object> entry : headers.entrySet())
+			builder.header(entry.getKey(), entry.getValue());
+	}
+
+	/**
+	 * Status 200
+	 */
+	public Response ok(T data) {
+		ResponseBuilder builder = Response.ok().entity(data);
+		addHeaders(builder);
+		return builder.build();
+	}
+
+	/**
+	 * Status 201
+	 */
+	public Response created(T data, URI loc) {
+		ResponseBuilder builder = Response.created(loc).entity(data);
+		addHeaders(builder);
+		return builder.build();
+	}
+
+	/**
+	 * Status 204
+	 */
+	public Response noContent() {
+		ResponseBuilder builder = Response.noContent();
+		addHeaders(builder);
+		return builder.build();
+	}
+
+	/**
+	 * Status 400
+	 */
+	public Response badRequest() {
+		ResponseBuilder builder = Response.status(Status.BAD_REQUEST).entity(errors);
+		addHeaders(builder);
+		return builder.build();
+	}
+
+	/**
+	 * Status 404
+	 */
+	public Response notFound() {
+		ResponseBuilder builder = Response.status(Status.NOT_FOUND).entity(errors);
+		addHeaders(builder);
+		return builder.build();
+	}
+
+	/**
+	 * Status 500
+	 */
+	public Response serverError() {
+		ResponseBuilder builder = Response.serverError().entity(errors);
+		addHeaders(builder);
+		return builder.build();
+	}
+
+}
diff --git a/vipra-backend/src/main/java/de/vipra/rest/model/Wrapper.java b/vipra-backend/src/main/java/de/vipra/rest/model/Wrapper.java
deleted file mode 100644
index d10e083018ab5274770762564c8ba7c8435251c1..0000000000000000000000000000000000000000
--- a/vipra-backend/src/main/java/de/vipra/rest/model/Wrapper.java
+++ /dev/null
@@ -1,165 +0,0 @@
-package de.vipra.rest.model;
-
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.Response.ResponseBuilder;
-import javax.ws.rs.core.Response.Status;
-import javax.ws.rs.core.UriBuilder;
-
-import de.vipra.rest.Messages;
-import de.vipra.util.NestedMap;
-
-public class Wrapper<T> {
-
-	private T data;
-	private List<APIError> errors;
-	private Map<String, String> links;
-	private NestedMap meta;
-
-	public Wrapper() {}
-
-	public Wrapper(T data) {
-		setData(data);
-	}
-
-	public Wrapper(APIError error) {
-		addError(error);
-	}
-
-	public T getData() {
-		return data;
-	}
-
-	public void setData(T data) {
-		this.data = data;
-		this.errors = null;
-	}
-
-	public List<APIError> getErrors() {
-		return errors;
-	}
-
-	public void setErrors(List<APIError> errors) {
-		this.errors = errors;
-		this.data = null;
-	}
-
-	public void addError(APIError error) {
-		if (errors == null)
-			errors = new ArrayList<>();
-		errors.add(error);
-	}
-
-	public Map<String, String> getLinks() {
-		return links;
-	}
-
-	public void setLinks(Map<String, String> links) {
-		this.links = links;
-	}
-
-	public void addLink(String key, String link) {
-		if (links == null)
-			links = new HashMap<>();
-		links.put(key, link);
-	}
-
-	public NestedMap getMeta() {
-		return meta;
-	}
-
-	public void setMeta(NestedMap meta) {
-		this.meta = meta;
-	}
-
-	public void addMeta(String key, Object value) {
-		if (meta == null)
-			meta = new NestedMap();
-		meta.put(key, value);
-	}
-
-	public void addPaginationLinks(URI base, Integer skip, Integer limit, long count) {
-		if (skip == null || limit == null || limit == 0)
-			return;
-
-		if (skip < 0) {
-			addError(new APIError(Response.Status.BAD_REQUEST, "Wrong skip number",
-					String.format(Messages.BAD_REQUEST, "skip number must be greater or equal to 0")));
-			return;
-		}
-
-		if (limit < 0) {
-			addError(new APIError(Response.Status.BAD_REQUEST, "Wrong limit size",
-					String.format(Messages.BAD_REQUEST, "when using skip, limit size must be greater or equal to 1")));
-			return;
-		}
-
-		addLink("first", UriBuilder.fromUri(base).queryParam("skip", 0).queryParam("limit", limit).build().toString());
-		addLink("last", UriBuilder.fromUri(base).queryParam("skip", (count / limit) * limit).queryParam("limit", limit)
-				.build().toString());
-
-		if (skip > 0) {
-			int diff = skip % limit;
-			if (diff == 0)
-				diff = limit;
-			int prevSkip = Math.max(0, skip - diff);
-			addLink("prev", UriBuilder.fromUri(base).queryParam("skip", prevSkip).queryParam("limit", limit).build()
-					.toString());
-		}
-
-		if (skip + limit < count) {
-			int diff = limit - skip % limit;
-			if (diff == 0)
-				diff = limit;
-			int nextSkip = skip + diff;
-			addLink("next", UriBuilder.fromUri(base).queryParam("skip", nextSkip).queryParam("limit", limit).build()
-					.toString());
-		}
-	}
-
-	public boolean hasErrors() {
-		return errors != null && errors.size() > 0;
-	}
-
-	public Response ok() {
-		return Response.ok().entity(this).build();
-	}
-
-	public Response ok(T data) {
-		this.data = data;
-		return ok();
-	}
-
-	public Response badRequest() {
-		return Response.status(Status.BAD_REQUEST).entity(this).build();
-	}
-
-	public Response serverError() {
-		return Response.serverError().entity(this).build();
-	}
-
-	public Response created(URI loc) {
-		return Response.created(loc).entity(this).build();
-	}
-
-	public Response notFound() {
-		return Response.status(Status.NOT_FOUND).entity(this).build();
-	}
-
-	public Response noContent() {
-		return Response.noContent().build();
-	}
-
-	public Response status(Status status, boolean withEntity) {
-		ResponseBuilder r = Response.status(status);
-		if (withEntity)
-			r.entity(this);
-		return r.build();
-	}
-
-}
diff --git a/vipra-backend/src/main/java/de/vipra/rest/provider/CORSResponseFilter.java b/vipra-backend/src/main/java/de/vipra/rest/provider/CORSResponseFilter.java
index 8e65b96201384a46edb7bc358bfe340a579482b2..ac8cc2e9a107af41095a25abf33755b3c04cb7f3 100644
--- a/vipra-backend/src/main/java/de/vipra/rest/provider/CORSResponseFilter.java
+++ b/vipra-backend/src/main/java/de/vipra/rest/provider/CORSResponseFilter.java
@@ -22,8 +22,9 @@ public class CORSResponseFilter implements ContainerResponseFilter {
 	@Override
 	public void filter(ContainerRequestContext request, ContainerResponseContext response) throws IOException {
 		response.getHeaders().add("Access-Control-Allow-Origin", "*");
-		response.getHeaders().add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization");
+		response.getHeaders().add("Access-Control-Allow-Headers", "Origin, Content-Type, Accept, Authorization");
 		response.getHeaders().add("Access-Control-Allow-Credentials", "true");
 		response.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD");
+		response.getHeaders().add("Access-Control-Expose-Headers", "V-Total");
 	}
 }
\ No newline at end of file
diff --git a/vipra-backend/src/main/java/de/vipra/rest/resource/ArticleResource.java b/vipra-backend/src/main/java/de/vipra/rest/resource/ArticleResource.java
index 9c88f448c81d52fafe18fa9ca6ebac11c62cbb9f..d37c7e365ef425c30208a1b1906a85977c34d069 100644
--- a/vipra-backend/src/main/java/de/vipra/rest/resource/ArticleResource.java
+++ b/vipra-backend/src/main/java/de/vipra/rest/resource/ArticleResource.java
@@ -27,7 +27,7 @@ import org.bson.types.ObjectId;
 
 import de.vipra.rest.Messages;
 import de.vipra.rest.model.APIError;
-import de.vipra.rest.model.Wrapper;
+import de.vipra.rest.model.ResponseWrapper;
 import de.vipra.util.Config;
 import de.vipra.util.MongoUtils;
 import de.vipra.util.StringUtils;
@@ -54,10 +54,7 @@ public class ArticleResource {
 	public Response getArticles(@QueryParam("skip") Integer skip, @QueryParam("limit") Integer limit,
 			@QueryParam("sort") @DefaultValue("date") String sortBy, @QueryParam("fields") String fields,
 			@QueryParam("query") String query) {
-		Wrapper<List<ArticleFull>> res = new Wrapper<>();
-
-		if (skip != null && limit != null)
-			res.addPaginationLinks(uri.getAbsolutePath(), skip, limit, dbArticles.count());
+		ResponseWrapper<List<ArticleFull>> res = new ResponseWrapper<>();
 
 		if (res.hasErrors())
 			return res.badRequest();
@@ -66,9 +63,9 @@ public class ArticleResource {
 			List<ArticleFull> articles = dbArticles.getMultiple(skip, limit, sortBy, StringUtils.getFields(fields));
 
 			if ((skip != null && skip > 0) || (limit != null && limit > 0))
-				res.addMeta("total", dbArticles.count());
+				res.addHeader("total", dbArticles.count());
 			else
-				res.addMeta("total", articles.size());
+				res.addHeader("total", articles.size());
 
 			return res.ok(articles);
 		} catch (Exception e) {
@@ -83,7 +80,7 @@ public class ArticleResource {
 	@Consumes(MediaType.APPLICATION_JSON)
 	@Path("{id}")
 	public Response getArticle(@PathParam("id") String id, @QueryParam("fields") String fields) {
-		Wrapper<ArticleFull> res = new Wrapper<>();
+		ResponseWrapper<ArticleFull> res = new ResponseWrapper<>();
 		if (id == null || id.trim().length() == 0) {
 			res.addError(new APIError(Response.Status.BAD_REQUEST, "ID is empty",
 					String.format(Messages.BAD_REQUEST, "id cannot be empty")));
@@ -112,15 +109,14 @@ public class ArticleResource {
 	@Consumes(MediaType.APPLICATION_JSON)
 	@Produces(MediaType.APPLICATION_JSON)
 	public Response createArticle(ArticleFull article) {
-		Wrapper<ArticleFull> res;
+		ResponseWrapper<ArticleFull> res = new ResponseWrapper<>();
 		try {
 			article = dbArticles.createSingle(article);
-			res = new Wrapper<>(article);
 			URI newUri = new URL(uri.getAbsolutePath().toURL(), article.getId().toString()).toURI();
-			return res.created(newUri);
+			return res.created(article, newUri);
 		} catch (DatabaseException | MalformedURLException | URISyntaxException e) {
 			e.printStackTrace();
-			res = new Wrapper<>(new APIError(Response.Status.INTERNAL_SERVER_ERROR, "item could not be created",
+			res.addError(new APIError(Response.Status.INTERNAL_SERVER_ERROR, "item could not be created",
 					"item could not be created due to an internal server error"));
 			return res.serverError();
 		}
@@ -129,13 +125,13 @@ public class ArticleResource {
 	@DELETE
 	@Path("{id}")
 	public Response deleteArticle(@PathParam("id") String id) {
-		Wrapper<ArticleFull> res = new Wrapper<>();
+		ResponseWrapper<ArticleFull> res = new ResponseWrapper<>();
 		long deleted;
 		try {
 			deleted = dbArticles.deleteSingle(MongoUtils.objectId(id));
 		} catch (DatabaseException e) {
 			e.printStackTrace();
-			res = new Wrapper<>(new APIError(Response.Status.INTERNAL_SERVER_ERROR, "item could not be deleted",
+			res.addError(new APIError(Response.Status.INTERNAL_SERVER_ERROR, "item could not be deleted",
 					"item could not be created due to an internal server error"));
 			return res.serverError();
 		}
@@ -157,14 +153,14 @@ public class ArticleResource {
 	@Produces(MediaType.APPLICATION_JSON)
 	@Path("{id}")
 	public Response replaceArticle(@PathParam("id") String id, ArticleFull article) {
-		Wrapper<ArticleFull> res = new Wrapper<>();
+		ResponseWrapper<ArticleFull> res = new ResponseWrapper<>();
 
 		try {
 			dbArticles.replaceSingle(article);
 			return res.ok(article);
 		} catch (DatabaseException e) {
 			e.printStackTrace();
-			res = new Wrapper<>(new APIError(Response.Status.INTERNAL_SERVER_ERROR, "item could not be updated",
+			res.addError(new APIError(Response.Status.INTERNAL_SERVER_ERROR, "item could not be updated",
 					"item could not be updated due to an internal server error"));
 			return res.serverError();
 		}
diff --git a/vipra-backend/src/main/java/de/vipra/rest/resource/InfoResource.java b/vipra-backend/src/main/java/de/vipra/rest/resource/InfoResource.java
index 0ea760c5443687d23d77008224153b5dd95797da..9a9aac2ab7b70699c068167edcdc7daace3ada86 100644
--- a/vipra-backend/src/main/java/de/vipra/rest/resource/InfoResource.java
+++ b/vipra-backend/src/main/java/de/vipra/rest/resource/InfoResource.java
@@ -13,7 +13,7 @@ import javax.ws.rs.core.UriInfo;
 
 import org.bson.types.ObjectId;
 
-import de.vipra.rest.model.Wrapper;
+import de.vipra.rest.model.ResponseWrapper;
 import de.vipra.util.BuildInfo;
 import de.vipra.util.Config;
 import de.vipra.util.NestedMap;
@@ -32,7 +32,7 @@ public class InfoResource {
 	@GET
 	@Produces(MediaType.APPLICATION_JSON)
 	public Response getInfo() {
-		Wrapper<NestedMap> res = new Wrapper<>();
+		ResponseWrapper<NestedMap> res = new ResponseWrapper<>();
 		NestedMap info = new NestedMap();
 
 		try {
diff --git a/vipra-backend/src/main/java/de/vipra/rest/resource/SearchResource.java b/vipra-backend/src/main/java/de/vipra/rest/resource/SearchResource.java
index 17bd7c1e69d73742b6d57ca3e3275aa2c828ca88..a9600df9df1754c0e9afc2e9f9f9634d97957e6b 100644
--- a/vipra-backend/src/main/java/de/vipra/rest/resource/SearchResource.java
+++ b/vipra-backend/src/main/java/de/vipra/rest/resource/SearchResource.java
@@ -25,7 +25,7 @@ import org.elasticsearch.search.SearchHit;
 import org.elasticsearch.search.SearchHits;
 
 import de.vipra.rest.model.APIError;
-import de.vipra.rest.model.Wrapper;
+import de.vipra.rest.model.ResponseWrapper;
 import de.vipra.util.Config;
 import de.vipra.util.Constants;
 import de.vipra.util.ESClient;
@@ -51,7 +51,7 @@ public class SearchResource {
 	@Produces(MediaType.APPLICATION_JSON)
 	public Response doSearch(@QueryParam("skip") Integer skip, @QueryParam("limit") Integer limit,
 			@QueryParam("fields") String fields, @QueryParam("query") String query) {
-		Wrapper<List<ArticleFull>> res = new Wrapper<>();
+		ResponseWrapper<List<ArticleFull>> res = new ResponseWrapper<>();
 
 		if (skip == null || skip < 0)
 			skip = 0;
@@ -95,7 +95,7 @@ public class SearchResource {
 			articles.add(article);
 		}
 
-		res.addMeta("total", articles.size());
+		res.addHeader("total", articles.size());
 
 		return res.ok(articles);
 	}
diff --git a/vipra-backend/src/main/java/de/vipra/rest/resource/TopicResource.java b/vipra-backend/src/main/java/de/vipra/rest/resource/TopicResource.java
index 00726deebfd902581cc74d95a6aba8bf5163a41b..6a0d7bde5fdc71e57f17b988124d9865e4e4b0f7 100644
--- a/vipra-backend/src/main/java/de/vipra/rest/resource/TopicResource.java
+++ b/vipra-backend/src/main/java/de/vipra/rest/resource/TopicResource.java
@@ -23,7 +23,7 @@ import org.bson.types.ObjectId;
 
 import de.vipra.rest.Messages;
 import de.vipra.rest.model.APIError;
-import de.vipra.rest.model.Wrapper;
+import de.vipra.rest.model.ResponseWrapper;
 import de.vipra.util.Config;
 import de.vipra.util.MongoUtils;
 import de.vipra.util.StringUtils;
@@ -56,10 +56,7 @@ public class TopicResource {
 	@Produces(MediaType.APPLICATION_JSON)
 	public Response getTopics(@QueryParam("skip") Integer skip, @QueryParam("limit") Integer limit,
 			@QueryParam("sort") @DefaultValue("name") String sortBy, @QueryParam("fields") String fields) {
-		Wrapper<List<TopicFull>> res = new Wrapper<>();
-
-		if (skip != null && limit != null)
-			res.addPaginationLinks(uri.getAbsolutePath(), skip, limit, dbTopics.count());
+		ResponseWrapper<List<TopicFull>> res = new ResponseWrapper<>();
 
 		if (res.hasErrors())
 			return Response.status(Response.Status.BAD_REQUEST).entity(res).build();
@@ -68,9 +65,9 @@ public class TopicResource {
 			List<TopicFull> topics = dbTopics.getMultiple(skip, limit, sortBy, StringUtils.getFields(fields));
 
 			if ((skip != null && skip > 0) || (limit != null && limit > 0))
-				res.addMeta("total", dbTopics.count());
+				res.addHeader("total", dbTopics.count());
 			else
-				res.addMeta("total", topics.size());
+				res.addHeader("total", topics.size());
 
 			return res.ok(topics);
 		} catch (Exception e) {
@@ -86,7 +83,7 @@ public class TopicResource {
 	@Path("{id}")
 	public Response getTopic(@PathParam("id") String id, @QueryParam("fields") String fields)
 			throws ConfigException, IOException {
-		Wrapper<TopicFull> res = new Wrapper<>();
+		ResponseWrapper<TopicFull> res = new ResponseWrapper<>();
 		if (id == null || id.trim().length() == 0) {
 			res.addError(new APIError(Response.Status.BAD_REQUEST, "ID is empty",
 					String.format(Messages.BAD_REQUEST, "id cannot be empty")));
@@ -116,7 +113,7 @@ public class TopicResource {
 	@Consumes(MediaType.APPLICATION_JSON)
 	@Path("{id}/articles")
 	public Response getArticles(@PathParam("id") String id, @QueryParam("fields") String fields) {
-		Wrapper<List<ArticleFull>> res = new Wrapper<>();
+		ResponseWrapper<List<ArticleFull>> res = new ResponseWrapper<>();
 		try {
 			Topic topic = new Topic(MongoUtils.objectId(id));
 			QueryBuilder query = QueryBuilder.builder().criteria("topics.topic", topic);
@@ -136,7 +133,7 @@ public class TopicResource {
 	@Produces(MediaType.APPLICATION_JSON)
 	@Path("{id}")
 	public Response replaceTopic(@PathParam("id") String id, TopicFull topic) {
-		Wrapper<TopicFull> res = new Wrapper<>();
+		ResponseWrapper<TopicFull> res = new ResponseWrapper<>();
 
 		try {
 			dbTopics.replaceSingle(topic);
diff --git a/vipra-backend/src/main/java/de/vipra/rest/resource/WordResource.java b/vipra-backend/src/main/java/de/vipra/rest/resource/WordResource.java
index 6e15887984f366576be421f248b27105b4cd61b2..951e398c4d982968ebfed3aea7fd0b45dd9b173a 100644
--- a/vipra-backend/src/main/java/de/vipra/rest/resource/WordResource.java
+++ b/vipra-backend/src/main/java/de/vipra/rest/resource/WordResource.java
@@ -20,7 +20,7 @@ import org.bson.types.ObjectId;
 
 import de.vipra.rest.Messages;
 import de.vipra.rest.model.APIError;
-import de.vipra.rest.model.Wrapper;
+import de.vipra.rest.model.ResponseWrapper;
 import de.vipra.util.Config;
 import de.vipra.util.StringUtils;
 import de.vipra.util.ex.ConfigException;
@@ -48,10 +48,7 @@ public class WordResource {
 	@Produces(MediaType.APPLICATION_JSON)
 	public Response getWords(@QueryParam("skip") Integer skip, @QueryParam("limit") Integer limit,
 			@QueryParam("sort") @DefaultValue("id") String sortBy, @QueryParam("fields") String fields) {
-		Wrapper<List<Word>> res = new Wrapper<>();
-
-		if (skip != null && limit != null)
-			res.addPaginationLinks(uri.getAbsolutePath(), skip, limit, dbWords.count());
+		ResponseWrapper<List<Word>> res = new ResponseWrapper<>();
 
 		if (res.hasErrors())
 			return res.badRequest();
@@ -60,9 +57,9 @@ public class WordResource {
 			List<Word> words = dbWords.getMultiple(skip, limit, sortBy, StringUtils.getFields(fields));
 
 			if ((skip != null && skip > 0) || (limit != null && limit > 0))
-				res.addMeta("total", dbWords.count());
+				res.addHeader("total", dbWords.count());
 			else
-				res.addMeta("total", words.size());
+				res.addHeader("total", words.size());
 
 			return res.ok(words);
 		} catch (Exception e) {
@@ -77,7 +74,7 @@ public class WordResource {
 	@Consumes(MediaType.APPLICATION_JSON)
 	@Path("{id}")
 	public Response getWord(@PathParam("id") String id, @QueryParam("fields") String fields) {
-		Wrapper<Word> res = new Wrapper<>();
+		ResponseWrapper<Word> res = new ResponseWrapper<>();
 		if (id == null || id.trim().length() == 0) {
 			res.addError(new APIError(Response.Status.BAD_REQUEST, "ID is empty",
 					String.format(Messages.BAD_REQUEST, "id cannot be empty")));
@@ -107,7 +104,7 @@ public class WordResource {
 	@Consumes(MediaType.APPLICATION_JSON)
 	@Path("{id}/topics")
 	public Response getWordTopics(@PathParam("id") String id, @QueryParam("fields") String fields) {
-		Wrapper<List<TopicFull>> res = new Wrapper<>();
+		ResponseWrapper<List<TopicFull>> res = new ResponseWrapper<>();
 		try {
 			Word word = new Word(id);
 			QueryBuilder query = QueryBuilder.builder().fields(true, "id", "name").criteria("words.word", word);
diff --git a/vipra-ui/app/html/articles/index.html b/vipra-ui/app/html/articles/index.html
index 63dfe95c87af1bc81c6f9933fd3068c596b2257d..56c9970a6b708d062c15cb007f736b2d4a00cc4d 100644
--- a/vipra-ui/app/html/articles/index.html
+++ b/vipra-ui/app/html/articles/index.html
@@ -1,6 +1,6 @@
 <div ui-view ng-cloak>
   <div class="well">
-    Found <span ng-bind="articlesMeta.total"></span> articles in the database <query-time/>.<br>
+    Found <span ng-bind="articlesTotal"></span> articles in the database.<br>
   </div>
 
   <ul class="dashed">
@@ -9,5 +9,5 @@
     </li>
   </ul>
 
-  <pagination total="articlesMeta.total" page="page" limit="limit" change="changePage"/>
+  <pagination total="articlesTotal" page="page" limit="limit" change="changePage"/>
 </div>
\ No newline at end of file
diff --git a/vipra-ui/app/html/articles/show.html b/vipra-ui/app/html/articles/show.html
index 52c390857ebe7e97d9bce7e419544d894f548156..800544880be9541add49170827bd7b7da20e8b0a 100644
--- a/vipra-ui/app/html/articles/show.html
+++ b/vipra-ui/app/html/articles/show.html
@@ -1,4 +1,4 @@
-<div ui-view ng-cloak>
+<div ng-cloak>
   <div class="page-header">
     <h1 ng-bind="::article.title"></h1>
 
diff --git a/vipra-ui/app/html/index.html b/vipra-ui/app/html/index.html
index b183c4b8a12f8b9b646cab6e3771eace06239405..d6be6e5e35a27c94a48f0215b73a78092d63de2e 100644
--- a/vipra-ui/app/html/index.html
+++ b/vipra-ui/app/html/index.html
@@ -1,4 +1,4 @@
-<div ui-view ng-cloak>
+<div ng-cloak>
   <div class="container">
 
     <div class="row" ng-hide="search">
@@ -37,9 +37,6 @@
     <div class="row row-spaced">
       <div class="col-md-12">
         <input type="text" class="form-control input-lg" placeholder="Search..." ng-model="search" ng-model-options="{debounce:500}">
-        <div id="advanced" style="display:none">
-            
-        </div>
       </div>
     </div>
 
@@ -48,10 +45,10 @@
         Searching...
       </div>
       <div class="col-md-12" ng-show="!searching && search && (!searchResults || searchResults.length == 0)">
-        <h4>No Results <query-time/></h4>
+        <h4>No Results</h4>
       </div>
       <div class="col-md-12" ng-show="searchResults.length > 0">
-        <h4>Results <query-time/></h4>
+        <h4>Results</h4>
         <ul class="list-unstyled search-results">
           <li class="search-result" ng-repeat="article in searchResults">
             <a ui-sref="articles.show({id:article.id})" ng-bind="article.title"></a>
diff --git a/vipra-ui/app/html/topics/articles.html b/vipra-ui/app/html/topics/articles.html
index 7b188562b7717c706b8fb32c0e04b886e0cd51bd..90d1e2d06a4928dc4ed81347ce5eec03f01acc01 100644
--- a/vipra-ui/app/html/topics/articles.html
+++ b/vipra-ui/app/html/topics/articles.html
@@ -1,4 +1,4 @@
-<div ui-view ng-cloak>
+<div ng-cloak>
   <div class="page-header">
     <h1>
       <div ng-bind="topic.name" ng-hide="isRename"></div>
diff --git a/vipra-ui/app/html/topics/index.html b/vipra-ui/app/html/topics/index.html
index 4997b5e93bc2e01e973344282156568378846d43..bf4b95edc2a8b7604134d25116ca2a24c6b7ec91 100644
--- a/vipra-ui/app/html/topics/index.html
+++ b/vipra-ui/app/html/topics/index.html
@@ -1,6 +1,6 @@
 <div ui-view ng-cloak>
   <div class="well">
-      Found <span ng-bind="topicsMeta.total"></span> topics in the database <query-time/>.
+      Found <span ng-bind="topicsTotal"></span> topics in the database.
   </div>
 
   <ul class="dashed">
@@ -9,5 +9,5 @@
     </li>
   </ul>
 
-  <pagination total="topicsMeta.total" page="page" limit="limit" change="changePage"/>
+  <pagination total="topicsTotal" page="page" limit="limit" change="changePage"/>
 </div>
\ No newline at end of file
diff --git a/vipra-ui/app/html/topics/show.html b/vipra-ui/app/html/topics/show.html
index 20a53ba7e89f4019bace2f6dffc8245546aa8d1d..964d974382856a7a1d3d1c6d998e644e76daf2ec 100644
--- a/vipra-ui/app/html/topics/show.html
+++ b/vipra-ui/app/html/topics/show.html
@@ -1,4 +1,4 @@
-<div ui-view ng-cloak>
+<div ng-cloak>
   <div class="page-header">
     <h1>
       <div ng-bind="topic.name" ng-hide="isRename"></div>
diff --git a/vipra-ui/app/html/words/index.html b/vipra-ui/app/html/words/index.html
index 4f6df953b5f2487fdbbf93a7dbb54f88b160baa4..7e129d75dd43cde067fc23adfcebc4f7d2db35c8 100644
--- a/vipra-ui/app/html/words/index.html
+++ b/vipra-ui/app/html/words/index.html
@@ -1,6 +1,6 @@
 <div ui-view ng-cloak>
   <div class="well">
-    Found <span ng-bind="wordsMeta.total"></span> words in the database <query-time/>.
+    Found <span ng-bind="wordsTotal"></span> words in the database.
   </div>
 
   <div class="row">
@@ -27,5 +27,5 @@
     </div>
   </div>
 
-  <pagination total="wordsMeta.total" page="page" limit="limit" change="changePage"/>
+  <pagination total="wordsTotal" page="page" limit="limit" change="changePage"/>
 </div>
\ No newline at end of file
diff --git a/vipra-ui/app/html/words/show.html b/vipra-ui/app/html/words/show.html
index 6174e8c3554e362f7ed5560f5a9bdf0fb4deef49..445cc90ae99c4df67e0c1c393f39dcbf3ee5298f 100644
--- a/vipra-ui/app/html/words/show.html
+++ b/vipra-ui/app/html/words/show.html
@@ -1,4 +1,4 @@
-<div ui-view ng-cloak>
+<div ng-cloak>
   <div class="page-header">
     <h1 ng-bind="::word.id"></h1>
   </div>
diff --git a/vipra-ui/app/index.html b/vipra-ui/app/index.html
index c13bd0d49afcf71e23a482e41d54e28329aef4f7..30b113bd77faf1fbde92cd76043925d347bcc7b3 100644
--- a/vipra-ui/app/index.html
+++ b/vipra-ui/app/index.html
@@ -59,7 +59,7 @@
           <ul class="nav navbar-nav navbar-right">
             <li ng-class="{active:$state.includes('about')}">
               <a ui-sref="about">
-              <span class="glyphicon glyphicon-question-sign"></span>
+                <span class="glyphicon glyphicon-question-sign"></span>
               </a>
             </li>
           </ul>
diff --git a/vipra-ui/app/js/app.js b/vipra-ui/app/js/app.js
index ec274b7df27d80135b16ae32ceaa85e19e092831..a2d1b64e3e24780e193d14ff686bb8e8699ccd74 100644
--- a/vipra-ui/app/js/app.js
+++ b/vipra-ui/app/js/app.js
@@ -74,7 +74,7 @@
       url: '/articles',
       templateUrl: Vipra.const.tplBase + '/topics/articles.html',
       controller: 'TopicsArticlesController'
-    })
+    });
 
     // states: words
 
@@ -120,7 +120,6 @@
       return {
         request: function(config) {
           requestIncrement(config);
-          config.$queryTime = performance.now();
           return config;
         },
 
@@ -131,7 +130,6 @@
 
         response: function(response) {
           requestDecrement(response.config);
-          response.data.$queryTime = (performance.now() - response.config.$queryTime).toFixed(2);
           return response;
         },
 
diff --git a/vipra-ui/app/js/config.js b/vipra-ui/app/js/config.js
index 67ae35dcc03490f07c31be2baef975e706f60100..50df7e92d2b6b2f912a8aa287bb73a98c83688bd 100644
--- a/vipra-ui/app/js/config.js
+++ b/vipra-ui/app/js/config.js
@@ -3,8 +3,8 @@
   window.Vipra = window.Vipra || {};
 
   Vipra.config = {
-    restUrl: '//' + location.hostname + ':8000/vipra/rest',
-    websocketUrl: '//' + location.hostname + ':8000/vipra/ws'
+    restUrl: '//' + location.hostname + ':8080/vipra/rest',
+    websocketUrl: '//' + location.hostname + ':8080/vipra/ws'
   };
 
 })();
\ No newline at end of file
diff --git a/vipra-ui/app/js/controllers.js b/vipra-ui/app/js/controllers.js
index 739050f1a441182731fc0a676bb83f8fb5bfa5b5..6d3302fc241007aeeffbdf590dcdb0bfeaa95863 100644
--- a/vipra-ui/app/js/controllers.js
+++ b/vipra-ui/app/js/controllers.js
@@ -21,26 +21,25 @@
 
     $scope.search = $location.search().query;
 
-    ArticleFactory.query({limit:Vipra.const.latestItems, sort:'-created'}, function(response) {
-      $scope.latestArticles = response.data;
+    ArticleFactory.query({limit:Vipra.const.latestItems, sort:'-created'}, function(data) {
+      $scope.latestArticles = data;
     });
 
-    TopicFactory.query({limit:Vipra.const.latestItems, sort:'-created'}, function(response) {
-      $scope.latestTopics = response.data;
+    TopicFactory.query({limit:Vipra.const.latestItems, sort:'-created'}, function(data) {
+      $scope.latestTopics = data;
     });
 
-    WordFactory.query({limit:Vipra.const.latestItems, sort:'-created'}, function(response) {
-      $scope.latestWords = response.data;
+    WordFactory.query({limit:Vipra.const.latestItems, sort:'-created'}, function(data) {
+      $scope.latestWords = data;
     });
 
     $scope.$watch('search', function() {
       if($scope.search) {
         $location.search('query', $scope.search);
         $scope.searching = true;
-        SearchFactory.query({limit:Vipra.const.searchResults, query:$scope.search}, function(response) {
+        SearchFactory.query({limit:Vipra.const.searchResults, query:$scope.search}, function(data) {
           $scope.searching = false;
-          $scope.searchResults = response.data;
-          $scope.queryTime = response.$queryTime;
+          $scope.searchResults = data;
         });
       } else {
         $location.search('query', null);
@@ -56,8 +55,8 @@
   app.controller('AboutController', ['$scope', 'InfoFactory',
     function($scope, InfoFactory) {
 
-    InfoFactory.get(function(response) {
-      $scope.info = response.data;
+    InfoFactory.get(function(data) {
+      $scope.info = data;
       $scope.buildDate = Vipra.formatDateTime(moment($scope.info.app.builddate, 'YYMMDD_HHmm').toDate());
       $scope.startTime = Vipra.formatDateTime(moment($scope.info.vm.starttime, 'x').toDate());
       $scope.upTime = moment.duration($scope.info.vm.uptime).humanize();
@@ -120,15 +119,15 @@
     }
 
     // get root node
-    factory.get({id: $stateParams.id}, function(response) {
+    factory.get({id: $stateParams.id}, function(data) {
       // add root node
       if($stateParams.type === 'articles')
-        $scope.nodes.add([articleNode(response.data)]);
+        $scope.nodes.add([articleNode(data)]);
       else if($stateParams.type === 'topics')
-        $scope.nodes.add([topicNode(response.data)]);
+        $scope.nodes.add([topicNode(data)]);
       else if($stateParams.type === 'words')
-        $scope.nodes.add([wordNode(response.data)]);
-      ids[response.data.id] = id;
+        $scope.nodes.add([wordNode(data)]);
+      ids[data.id] = id;
 
       // create graph
       $scope.graph = new vis.Network(container, $scope.data, $scope.options);
@@ -219,25 +218,25 @@
         if(node) {
           if(node.type === 'article' && $scope.shown.topics) {
             // node is article, load article to get topics
-            ArticleFactory.get({id:node.dbid}, function(res) {
-              for(var i = 0; i < res.data.topics.length; i++)
-                res.data.topics[i] = res.data.topics[i].topic;
-              constructor(res, node, 'topics', topicNode);
+            ArticleFactory.get({id:node.dbid}, function(data) {
+              for(var i = 0; i < data.topics.length; i++)
+                data.topics[i] = res.data.topics[i].topic;
+              constructor(data, node, 'topics', topicNode);
             });
           } else if(node.type === 'topic') {
             // node is topic, load topic to get words and articles
             if($scope.shown.words)
-              TopicFactory.get({id:node.dbid}, function(res) {
-                constructor(res, node, 'words', wordNode);
+              TopicFactory.get({id:node.dbid}, function(data) {
+                constructor(data, node, 'words', wordNode);
               });
             if($scope.shown.articles)
-              TopicFactory.articles({id:node.dbid}, function(res) {
-                constructor(res, node, null, articleNode);
+              TopicFactory.articles({id:node.dbid}, function(data) {
+                constructor(data, node, null, articleNode);
               });
           } else if(node.type === 'word' && $scope.shown.topics) {
             // node is word, load word to get topics
-            WordFactory.get({id:node.dbid}, function(res) {
-              constructor(res, node, 'topics', topicNode);
+            WordFactory.get({id:node.dbid}, function(data) {
+              constructor(data, node, 'topics', topicNode);
             });
           }
           $scope.nodes.update(node);
@@ -274,10 +273,9 @@
         skip: ($scope.page-1)*$scope.limit,
         limit: $scope.limit,
         sort: $scope.order+$scope.sort
-      }, function(response) {
-        $scope.articles = response.data;
-        $scope.articlesMeta = response.meta;
-        $scope.queryTime = response.$queryTime;
+      }, function(data, headers) {
+        $scope.articles = data;
+        $scope.articlesTotal = headers("V-Total");
       });
     };
 
@@ -299,14 +297,12 @@
     $scope.topicSort = $scope.topicSort || 'topic.share';
     $scope.topicSortRev = typeof $scope.topicSortRev === 'undefined' ? false : $scope.topicSortRev;
 
-    ArticleFactory.get({id: $stateParams.id}, function(response) {
-      $scope.article = response.data;
+    ArticleFactory.get({id: $stateParams.id}, function(data) {
+      $scope.article = data;
       $scope.article.text = Vipra.createInitial($scope.article.text);
       $scope.articleDate = Vipra.formatDate($scope.article.date);
       $scope.articleCreated = Vipra.formatDateTime($scope.article.created);
       $scope.articleModified = Vipra.formatDateTime($scope.article.modified);
-      $scope.articleMeta = response.meta;
-      $scope.queryTime = response.$queryTime;
 
       // calculate percentage share
       var topicShareSeries = [],
@@ -358,10 +354,9 @@
         skip: ($scope.page-1)*$scope.limit,
         limit: $scope.limit,
         sort: $scope.order+$scope.sort
-      }, function(response) {
-        $scope.topics = response.data;
-        $scope.topicsMeta = response.meta;
-        $scope.queryTime = response.$queryTime;
+      }, function(data, headers) {
+        $scope.topics = data;
+        $scope.topicsTotal = headers("V-Total");
       });
     };
 
@@ -383,12 +378,10 @@
     $scope.wordSort = $scope.wordSort || 'likeliness';
     $scope.wordSortRev = typeof $scope.wordSortRev === 'undefined' ? true : $scope.wordSortRev;
 
-    TopicFactory.get({id: $stateParams.id}, function(response) {
-      $scope.topic = response.data;
+    TopicFactory.get({id: $stateParams.id}, function(data) {
+      $scope.topic = data;
       $scope.topicCreated = Vipra.formatDateTime($scope.topic.created);
       $scope.topicModified = Vipra.formatDateTime($scope.topic.modified);
-      $scope.topicMeta = response.meta;
-      $scope.queryTime = response.$queryTime;
     });
 
     $scope.startRename = function() {
@@ -402,12 +395,11 @@
     $scope.endRename = function(save) {
       delete $scope.renameErrors;
       if(save) {
-        TopicFactory.update({id:$scope.topic.id}, $scope.topic, function(response) {
-          $scope.topic = response.data;
+        TopicFactory.update({id:$scope.topic.id}, $scope.topic, function(data) {
+          $scope.topic = data;
           $scope.isRename = false;
-        }, function(response) {
-          if(response.data)
-          $scope.renameErrors = Vipra.getErrors(response.data.errors);
+        }, function(errors) {
+          $scope.renameErrors = Vipra.getErrors(errors);
         });
       } else {
         $scope.isRename = false;
@@ -430,9 +422,8 @@
   app.controller('TopicArticlesController', ['$scope', '$stateParams', 'TopicFactory',
     function($scope, $stateParams, TopicFactory) {
 
-    TopicFactory.articles({id: $stateParams.id}, function(response) {
-      $scope.articles = response.data;
-      $scope.queryTime = response.$queryTime;
+    TopicFactory.articles({id: $stateParams.id}, function(data) {
+      $scope.articles = data;
     });
 
   }]);
@@ -458,10 +449,9 @@
         limit: $scope.limit,
         sort: $scope.sort,
         sort: $scope.order+$scope.sort
-      }, function(response) {
-        $scope.words = response.data;
-        $scope.wordsMeta = response.meta;
-        $scope.queryTime = response.$queryTime;
+      }, function(data, headers) {
+        $scope.words = data;
+        $scope.wordsTotal = headers("V-Total");
       });
     };
 
@@ -480,15 +470,13 @@
   app.controller('WordsShowController', ['$scope', '$stateParams', 'WordFactory',
     function($scope, $stateParams, WordFactory) {
 
-    WordFactory.get({id: $stateParams.id}, function(response) {
-      $scope.word = response.data;
+    WordFactory.get({id: $stateParams.id}, function(data) {
+      $scope.word = data;
       $scope.wordCreated = Vipra.formatDateTime($scope.word.created);
-      $scope.wordMeta = response.meta;
-      $scope.queryTime = response.$queryTime;
     });
 
-    WordFactory.topics({id: $stateParams.id}, function(response) {
-      $scope.topics = response.data;
+    WordFactory.topics({id: $stateParams.id}, function(data) {
+      $scope.topics = data;
     });
 
   }]);
diff --git a/vipra-ui/app/js/directives.js b/vipra-ui/app/js/directives.js
index 48364ce22994f8a1cb73e01273289a21a1fe96ef..49bb69f92f8f138db052bec2937f57b052cb6aaf 100644
--- a/vipra-ui/app/js/directives.js
+++ b/vipra-ui/app/js/directives.js
@@ -32,14 +32,6 @@
     }
   });
 
-  app.directive('queryTime', function() {
-    return {
-      restrict: 'E',
-      replace: true,
-      template: '<small class="text-muted">(took <span ng-bind-template="{{queryTime}}ms"></span>)</small>'
-    };
-  });
-
   app.directive('pagination', function() {
     return {
       restrict: 'E',
diff --git a/vipra-ui/app/js/factories.js b/vipra-ui/app/js/factories.js
index b6f203282cd302538815b4920e0740dd4790222f..54b50fe55f4b5681cc1e7e35444216ab9acd54f4 100644
--- a/vipra-ui/app/js/factories.js
+++ b/vipra-ui/app/js/factories.js
@@ -7,30 +7,24 @@
   var app = angular.module('vipra.factories', []);
 
   app.factory('ArticleFactory', ['$resource', function($resource) {
-    return $resource(Vipra.config.restUrl + '/articles/:id', {}, {
-      query: { isArray: false }
-    });
+    return $resource(Vipra.config.restUrl + '/articles/:id');
   }]);
 
   app.factory('TopicFactory', ['$resource', function($resource) {
     return $resource(Vipra.config.restUrl + '/topics/:id', {}, {
-      query: { isArray: false },
       update: { method: 'PUT' },
-      articles: { url: Vipra.config.restUrl + '/topics/:id/articles' }
+      articles: { isArray: true, url: Vipra.config.restUrl + '/topics/:id/articles' }
     });
   }]);
 
   app.factory('WordFactory', ['$resource', function($resource) {
     return $resource(Vipra.config.restUrl + '/words/:id', {}, {
-      query: { isArray: false },
-      topics: { url: Vipra.config.restUrl + '/words/:id/topics' }
+      topics: { isArray: true, url: Vipra.config.restUrl + '/words/:id/topics' }
     });
   }]);
 
   app.factory('SearchFactory', ['$resource', function($resource) {
-    return $resource(Vipra.config.restUrl + '/search', {}, {
-      query: { isArray: false }
-    });
+    return $resource(Vipra.config.restUrl + '/search');
   }]);
 
   app.factory('InfoFactory', ['$resource', function($resource) {